ddeca6de02d1f7ced1108249b49629435a42ab03
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         
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         if (this.active) {
4085             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4086         }
4087         if (this.disabled) {
4088             cfg.cls += ' disabled';
4089         }
4090         
4091         if (this.href || this.html || this.glyphicon || this.icon) {
4092             cfg.cn = [
4093                 {
4094                     tag: this.tagtype,
4095                     href : this.href || "#",
4096                     html: this.html || ''
4097                 }
4098             ];
4099             
4100             if (this.icon) {
4101                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4102             }
4103
4104             if(this.glyphicon) {
4105                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4106             }
4107             
4108             if (this.menu) {
4109                 
4110                 cfg.cn[0].html += " <span class='caret'></span>";
4111              
4112             }
4113             
4114             if (this.badge !== '') {
4115                  
4116                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4117             }
4118         }
4119         
4120         
4121         
4122         return cfg;
4123     },
4124     initEvents: function() 
4125     {
4126         if (typeof (this.menu) != 'undefined') {
4127             this.menu.parentType = this.xtype;
4128             this.menu.triggerEl = this.el;
4129             this.menu = this.addxtype(Roo.apply({}, this.menu));
4130         }
4131         
4132         this.el.select('a',true).on('click', this.onClick, this);
4133         
4134         if(this.tagtype == 'span'){
4135             this.el.select('span',true).on('click', this.onClick, this);
4136         }
4137        
4138         // at this point parent should be available..
4139         this.parent().register(this);
4140     },
4141     
4142     onClick : function(e)
4143     {
4144         if(
4145                 this.preventDefault || 
4146                 this.href == '#' 
4147         ){
4148             
4149             e.preventDefault();
4150         }
4151         
4152         if (this.disabled) {
4153             return;
4154         }
4155         
4156         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4157         if (tg && tg.transition) {
4158             Roo.log("waiting for the transitionend");
4159             return;
4160         }
4161         
4162         
4163         
4164         //Roo.log("fire event clicked");
4165         if(this.fireEvent('click', this, e) === false){
4166             return;
4167         };
4168         
4169         if(this.tagtype == 'span'){
4170             return;
4171         }
4172         
4173         //Roo.log(this.href);
4174         var ael = this.el.select('a',true).first();
4175         //Roo.log(ael);
4176         
4177         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4178             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4179             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4180                 return; // ignore... - it's a 'hash' to another page.
4181             }
4182             
4183             e.preventDefault();
4184             this.scrollToElement(e);
4185         }
4186         
4187         
4188         var p =  this.parent();
4189    
4190         if (['tabs','pills'].indexOf(p.type)!==-1) {
4191             if (typeof(p.setActiveItem) !== 'undefined') {
4192                 p.setActiveItem(this);
4193             }
4194         }
4195         
4196         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4197         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4198             // remove the collapsed menu expand...
4199             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4200         }
4201     },
4202     
4203     isActive: function () {
4204         return this.active
4205     },
4206     setActive : function(state, fire, is_was_active)
4207     {
4208         if (this.active && !state && this.navId) {
4209             this.was_active = true;
4210             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4211             if (nv) {
4212                 nv.clearWasActive(this);
4213             }
4214             
4215         }
4216         this.active = state;
4217         
4218         if (!state ) {
4219             this.el.removeClass('active');
4220         } else if (!this.el.hasClass('active')) {
4221             this.el.addClass('active');
4222         }
4223         if (fire) {
4224             this.fireEvent('changed', this, state);
4225         }
4226         
4227         // show a panel if it's registered and related..
4228         
4229         if (!this.navId || !this.tabId || !state || is_was_active) {
4230             return;
4231         }
4232         
4233         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4234         if (!tg) {
4235             return;
4236         }
4237         var pan = tg.getPanelByName(this.tabId);
4238         if (!pan) {
4239             return;
4240         }
4241         // if we can not flip to new panel - go back to old nav highlight..
4242         if (false == tg.showPanel(pan)) {
4243             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4244             if (nv) {
4245                 var onav = nv.getWasActive();
4246                 if (onav) {
4247                     onav.setActive(true, false, true);
4248                 }
4249             }
4250             
4251         }
4252         
4253         
4254         
4255     },
4256      // this should not be here...
4257     setDisabled : function(state)
4258     {
4259         this.disabled = state;
4260         if (!state ) {
4261             this.el.removeClass('disabled');
4262         } else if (!this.el.hasClass('disabled')) {
4263             this.el.addClass('disabled');
4264         }
4265         
4266     },
4267     
4268     /**
4269      * Fetch the element to display the tooltip on.
4270      * @return {Roo.Element} defaults to this.el
4271      */
4272     tooltipEl : function()
4273     {
4274         return this.el.select('' + this.tagtype + '', true).first();
4275     },
4276     
4277     scrollToElement : function(e)
4278     {
4279         var c = document.body;
4280         
4281         /*
4282          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4283          */
4284         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4285             c = document.documentElement;
4286         }
4287         
4288         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4289         
4290         if(!target){
4291             return;
4292         }
4293
4294         var o = target.calcOffsetsTo(c);
4295         
4296         var options = {
4297             target : target,
4298             value : o[1]
4299         }
4300         
4301         this.fireEvent('scrollto', this, options, e);
4302         
4303         Roo.get(c).scrollTo('top', options.value, true);
4304         
4305         return;
4306     }
4307 });
4308  
4309
4310  /*
4311  * - LGPL
4312  *
4313  * sidebar item
4314  *
4315  *  li
4316  *    <span> icon </span>
4317  *    <span> text </span>
4318  *    <span>badge </span>
4319  */
4320
4321 /**
4322  * @class Roo.bootstrap.NavSidebarItem
4323  * @extends Roo.bootstrap.NavItem
4324  * Bootstrap Navbar.NavSidebarItem class
4325  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4326  * @constructor
4327  * Create a new Navbar Button
4328  * @param {Object} config The config object
4329  */
4330 Roo.bootstrap.NavSidebarItem = function(config){
4331     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4332     this.addEvents({
4333         // raw events
4334         /**
4335          * @event click
4336          * The raw click event for the entire grid.
4337          * @param {Roo.EventObject} e
4338          */
4339         "click" : true,
4340          /**
4341             * @event changed
4342             * Fires when the active item active state changes
4343             * @param {Roo.bootstrap.NavSidebarItem} this
4344             * @param {boolean} state the new state
4345              
4346          */
4347         'changed': true
4348     });
4349    
4350 };
4351
4352 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4353     
4354     badgeWeight : 'default',
4355     
4356     getAutoCreate : function(){
4357         
4358         
4359         var a = {
4360                 tag: 'a',
4361                 href : this.href || '#',
4362                 cls: '',
4363                 html : '',
4364                 cn : []
4365         };
4366         var cfg = {
4367             tag: 'li',
4368             cls: '',
4369             cn: [ a ]
4370         };
4371         var span = {
4372             tag: 'span',
4373             html : this.html || ''
4374         };
4375         
4376         
4377         if (this.active) {
4378             cfg.cls += ' active';
4379         }
4380         
4381         if (this.disabled) {
4382             cfg.cls += ' disabled';
4383         }
4384         
4385         // left icon..
4386         if (this.glyphicon || this.icon) {
4387             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4388             a.cn.push({ tag : 'i', cls : c }) ;
4389         }
4390         // html..
4391         a.cn.push(span);
4392         // then badge..
4393         if (this.badge !== '') {
4394             
4395             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4396         }
4397         // fi
4398         if (this.menu) {
4399             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4400             a.cls += 'dropdown-toggle treeview' ;
4401             
4402         }
4403         
4404         
4405         
4406         return cfg;
4407          
4408            
4409     },
4410     
4411     initEvents : function()
4412     { 
4413         this.el.on('click', this.onClick, this);
4414        
4415     
4416         if(this.badge !== ''){
4417  
4418             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4419         }
4420         
4421     },
4422     
4423     onClick : function(e)
4424     {
4425         if(this.disabled){
4426             e.preventDefault();
4427             return;
4428         }
4429         
4430         if(this.preventDefault){
4431             e.preventDefault();
4432         }
4433         
4434         this.fireEvent('click', this);
4435     },
4436     
4437     disable : function()
4438     {
4439         this.setDisabled(true);
4440     },
4441     
4442     enable : function()
4443     {
4444         this.setDisabled(false);
4445     },
4446     
4447     setDisabled : function(state)
4448     {
4449         if(this.disabled == state){
4450             return;
4451         }
4452         
4453         this.disabled = state;
4454         
4455         if (state) {
4456             this.el.addClass('disabled');
4457             return;
4458         }
4459         
4460         this.el.removeClass('disabled');
4461         
4462         return;
4463     },
4464     
4465     setActive : function(state)
4466     {
4467         if(this.active == state){
4468             return;
4469         }
4470         
4471         this.active = state;
4472         
4473         if (state) {
4474             this.el.addClass('active');
4475             return;
4476         }
4477         
4478         this.el.removeClass('active');
4479         
4480         return;
4481     },
4482     
4483     isActive: function () 
4484     {
4485         return this.active;
4486     },
4487     
4488     setBadge : function(str)
4489     {
4490         if(!this.badgeEl){
4491             return;
4492         }
4493         
4494         this.badgeEl.dom.innerHTML = str;
4495     }
4496     
4497    
4498      
4499  
4500 });
4501  
4502
4503  /*
4504  * - LGPL
4505  *
4506  * row
4507  * 
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.Row
4512  * @extends Roo.bootstrap.Component
4513  * Bootstrap Row class (contains columns...)
4514  * 
4515  * @constructor
4516  * Create a new Row
4517  * @param {Object} config The config object
4518  */
4519
4520 Roo.bootstrap.Row = function(config){
4521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4522 };
4523
4524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4525     
4526     getAutoCreate : function(){
4527        return {
4528             cls: 'row clearfix'
4529        };
4530     }
4531     
4532     
4533 });
4534
4535  
4536
4537  /*
4538  * - LGPL
4539  *
4540  * element
4541  * 
4542  */
4543
4544 /**
4545  * @class Roo.bootstrap.Element
4546  * @extends Roo.bootstrap.Component
4547  * Bootstrap Element class
4548  * @cfg {String} html contents of the element
4549  * @cfg {String} tag tag of the element
4550  * @cfg {String} cls class of the element
4551  * @cfg {Boolean} preventDefault (true|false) default false
4552  * @cfg {Boolean} clickable (true|false) default false
4553  * 
4554  * @constructor
4555  * Create a new Element
4556  * @param {Object} config The config object
4557  */
4558
4559 Roo.bootstrap.Element = function(config){
4560     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4561     
4562     this.addEvents({
4563         // raw events
4564         /**
4565          * @event click
4566          * When a element is chick
4567          * @param {Roo.bootstrap.Element} this
4568          * @param {Roo.EventObject} e
4569          */
4570         "click" : true
4571     });
4572 };
4573
4574 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4575     
4576     tag: 'div',
4577     cls: '',
4578     html: '',
4579     preventDefault: false, 
4580     clickable: false,
4581     
4582     getAutoCreate : function(){
4583         
4584         var cfg = {
4585             tag: this.tag,
4586             cls: this.cls,
4587             html: this.html
4588         }
4589         
4590         return cfg;
4591     },
4592     
4593     initEvents: function() 
4594     {
4595         Roo.bootstrap.Element.superclass.initEvents.call(this);
4596         
4597         if(this.clickable){
4598             this.el.on('click', this.onClick, this);
4599         }
4600         
4601     },
4602     
4603     onClick : function(e)
4604     {
4605         if(this.preventDefault){
4606             e.preventDefault();
4607         }
4608         
4609         this.fireEvent('click', this, e);
4610     },
4611     
4612     getValue : function()
4613     {
4614         return this.el.dom.innerHTML;
4615     },
4616     
4617     setValue : function(value)
4618     {
4619         this.el.dom.innerHTML = value;
4620     }
4621    
4622 });
4623
4624  
4625
4626  /*
4627  * - LGPL
4628  *
4629  * pagination
4630  * 
4631  */
4632
4633 /**
4634  * @class Roo.bootstrap.Pagination
4635  * @extends Roo.bootstrap.Component
4636  * Bootstrap Pagination class
4637  * @cfg {String} size xs | sm | md | lg
4638  * @cfg {Boolean} inverse false | true
4639  * 
4640  * @constructor
4641  * Create a new Pagination
4642  * @param {Object} config The config object
4643  */
4644
4645 Roo.bootstrap.Pagination = function(config){
4646     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4647 };
4648
4649 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4650     
4651     cls: false,
4652     size: false,
4653     inverse: false,
4654     
4655     getAutoCreate : function(){
4656         var cfg = {
4657             tag: 'ul',
4658                 cls: 'pagination'
4659         };
4660         if (this.inverse) {
4661             cfg.cls += ' inverse';
4662         }
4663         if (this.html) {
4664             cfg.html=this.html;
4665         }
4666         if (this.cls) {
4667             cfg.cls += " " + this.cls;
4668         }
4669         return cfg;
4670     }
4671    
4672 });
4673
4674  
4675
4676  /*
4677  * - LGPL
4678  *
4679  * Pagination item
4680  * 
4681  */
4682
4683
4684 /**
4685  * @class Roo.bootstrap.PaginationItem
4686  * @extends Roo.bootstrap.Component
4687  * Bootstrap PaginationItem class
4688  * @cfg {String} html text
4689  * @cfg {String} href the link
4690  * @cfg {Boolean} preventDefault (true | false) default true
4691  * @cfg {Boolean} active (true | false) default false
4692  * @cfg {Boolean} disabled default false
4693  * 
4694  * 
4695  * @constructor
4696  * Create a new PaginationItem
4697  * @param {Object} config The config object
4698  */
4699
4700
4701 Roo.bootstrap.PaginationItem = function(config){
4702     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4703     this.addEvents({
4704         // raw events
4705         /**
4706          * @event click
4707          * The raw click event for the entire grid.
4708          * @param {Roo.EventObject} e
4709          */
4710         "click" : true
4711     });
4712 };
4713
4714 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4715     
4716     href : false,
4717     html : false,
4718     preventDefault: true,
4719     active : false,
4720     cls : false,
4721     disabled: false,
4722     
4723     getAutoCreate : function(){
4724         var cfg= {
4725             tag: 'li',
4726             cn: [
4727                 {
4728                     tag : 'a',
4729                     href : this.href ? this.href : '#',
4730                     html : this.html ? this.html : ''
4731                 }
4732             ]
4733         };
4734         
4735         if(this.cls){
4736             cfg.cls = this.cls;
4737         }
4738         
4739         if(this.disabled){
4740             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4741         }
4742         
4743         if(this.active){
4744             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4745         }
4746         
4747         return cfg;
4748     },
4749     
4750     initEvents: function() {
4751         
4752         this.el.on('click', this.onClick, this);
4753         
4754     },
4755     onClick : function(e)
4756     {
4757         Roo.log('PaginationItem on click ');
4758         if(this.preventDefault){
4759             e.preventDefault();
4760         }
4761         
4762         if(this.disabled){
4763             return;
4764         }
4765         
4766         this.fireEvent('click', this, e);
4767     }
4768    
4769 });
4770
4771  
4772
4773  /*
4774  * - LGPL
4775  *
4776  * slider
4777  * 
4778  */
4779
4780
4781 /**
4782  * @class Roo.bootstrap.Slider
4783  * @extends Roo.bootstrap.Component
4784  * Bootstrap Slider class
4785  *    
4786  * @constructor
4787  * Create a new Slider
4788  * @param {Object} config The config object
4789  */
4790
4791 Roo.bootstrap.Slider = function(config){
4792     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4793 };
4794
4795 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4796     
4797     getAutoCreate : function(){
4798         
4799         var cfg = {
4800             tag: 'div',
4801             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4802             cn: [
4803                 {
4804                     tag: 'a',
4805                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4806                 }
4807             ]
4808         }
4809         
4810         return cfg;
4811     }
4812    
4813 });
4814
4815  /*
4816  * Based on:
4817  * Ext JS Library 1.1.1
4818  * Copyright(c) 2006-2007, Ext JS, LLC.
4819  *
4820  * Originally Released Under LGPL - original licence link has changed is not relivant.
4821  *
4822  * Fork - LGPL
4823  * <script type="text/javascript">
4824  */
4825  
4826
4827 /**
4828  * @class Roo.grid.ColumnModel
4829  * @extends Roo.util.Observable
4830  * This is the default implementation of a ColumnModel used by the Grid. It defines
4831  * the columns in the grid.
4832  * <br>Usage:<br>
4833  <pre><code>
4834  var colModel = new Roo.grid.ColumnModel([
4835         {header: "Ticker", width: 60, sortable: true, locked: true},
4836         {header: "Company Name", width: 150, sortable: true},
4837         {header: "Market Cap.", width: 100, sortable: true},
4838         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4839         {header: "Employees", width: 100, sortable: true, resizable: false}
4840  ]);
4841  </code></pre>
4842  * <p>
4843  
4844  * The config options listed for this class are options which may appear in each
4845  * individual column definition.
4846  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4847  * @constructor
4848  * @param {Object} config An Array of column config objects. See this class's
4849  * config objects for details.
4850 */
4851 Roo.grid.ColumnModel = function(config){
4852         /**
4853      * The config passed into the constructor
4854      */
4855     this.config = config;
4856     this.lookup = {};
4857
4858     // if no id, create one
4859     // if the column does not have a dataIndex mapping,
4860     // map it to the order it is in the config
4861     for(var i = 0, len = config.length; i < len; i++){
4862         var c = config[i];
4863         if(typeof c.dataIndex == "undefined"){
4864             c.dataIndex = i;
4865         }
4866         if(typeof c.renderer == "string"){
4867             c.renderer = Roo.util.Format[c.renderer];
4868         }
4869         if(typeof c.id == "undefined"){
4870             c.id = Roo.id();
4871         }
4872         if(c.editor && c.editor.xtype){
4873             c.editor  = Roo.factory(c.editor, Roo.grid);
4874         }
4875         if(c.editor && c.editor.isFormField){
4876             c.editor = new Roo.grid.GridEditor(c.editor);
4877         }
4878         this.lookup[c.id] = c;
4879     }
4880
4881     /**
4882      * The width of columns which have no width specified (defaults to 100)
4883      * @type Number
4884      */
4885     this.defaultWidth = 100;
4886
4887     /**
4888      * Default sortable of columns which have no sortable specified (defaults to false)
4889      * @type Boolean
4890      */
4891     this.defaultSortable = false;
4892
4893     this.addEvents({
4894         /**
4895              * @event widthchange
4896              * Fires when the width of a column changes.
4897              * @param {ColumnModel} this
4898              * @param {Number} columnIndex The column index
4899              * @param {Number} newWidth The new width
4900              */
4901             "widthchange": true,
4902         /**
4903              * @event headerchange
4904              * Fires when the text of a header changes.
4905              * @param {ColumnModel} this
4906              * @param {Number} columnIndex The column index
4907              * @param {Number} newText The new header text
4908              */
4909             "headerchange": true,
4910         /**
4911              * @event hiddenchange
4912              * Fires when a column is hidden or "unhidden".
4913              * @param {ColumnModel} this
4914              * @param {Number} columnIndex The column index
4915              * @param {Boolean} hidden true if hidden, false otherwise
4916              */
4917             "hiddenchange": true,
4918             /**
4919          * @event columnmoved
4920          * Fires when a column is moved.
4921          * @param {ColumnModel} this
4922          * @param {Number} oldIndex
4923          * @param {Number} newIndex
4924          */
4925         "columnmoved" : true,
4926         /**
4927          * @event columlockchange
4928          * Fires when a column's locked state is changed
4929          * @param {ColumnModel} this
4930          * @param {Number} colIndex
4931          * @param {Boolean} locked true if locked
4932          */
4933         "columnlockchange" : true
4934     });
4935     Roo.grid.ColumnModel.superclass.constructor.call(this);
4936 };
4937 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4938     /**
4939      * @cfg {String} header The header text to display in the Grid view.
4940      */
4941     /**
4942      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4943      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4944      * specified, the column's index is used as an index into the Record's data Array.
4945      */
4946     /**
4947      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4948      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4949      */
4950     /**
4951      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4952      * Defaults to the value of the {@link #defaultSortable} property.
4953      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4954      */
4955     /**
4956      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4957      */
4958     /**
4959      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4960      */
4961     /**
4962      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4963      */
4964     /**
4965      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4966      */
4967     /**
4968      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4969      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4970      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4971      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4972      */
4973        /**
4974      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4975      */
4976     /**
4977      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4978      */
4979     /**
4980      * @cfg {String} cursor (Optional)
4981      */
4982     /**
4983      * @cfg {String} tooltip (Optional)
4984      */
4985     /**
4986      * @cfg {Number} xs (Optional)
4987      */
4988     /**
4989      * @cfg {Number} sm (Optional)
4990      */
4991     /**
4992      * @cfg {Number} md (Optional)
4993      */
4994     /**
4995      * @cfg {Number} lg (Optional)
4996      */
4997     /**
4998      * Returns the id of the column at the specified index.
4999      * @param {Number} index The column index
5000      * @return {String} the id
5001      */
5002     getColumnId : function(index){
5003         return this.config[index].id;
5004     },
5005
5006     /**
5007      * Returns the column for a specified id.
5008      * @param {String} id The column id
5009      * @return {Object} the column
5010      */
5011     getColumnById : function(id){
5012         return this.lookup[id];
5013     },
5014
5015     
5016     /**
5017      * Returns the column for a specified dataIndex.
5018      * @param {String} dataIndex The column dataIndex
5019      * @return {Object|Boolean} the column or false if not found
5020      */
5021     getColumnByDataIndex: function(dataIndex){
5022         var index = this.findColumnIndex(dataIndex);
5023         return index > -1 ? this.config[index] : false;
5024     },
5025     
5026     /**
5027      * Returns the index for a specified column id.
5028      * @param {String} id The column id
5029      * @return {Number} the index, or -1 if not found
5030      */
5031     getIndexById : function(id){
5032         for(var i = 0, len = this.config.length; i < len; i++){
5033             if(this.config[i].id == id){
5034                 return i;
5035             }
5036         }
5037         return -1;
5038     },
5039     
5040     /**
5041      * Returns the index for a specified column dataIndex.
5042      * @param {String} dataIndex The column dataIndex
5043      * @return {Number} the index, or -1 if not found
5044      */
5045     
5046     findColumnIndex : function(dataIndex){
5047         for(var i = 0, len = this.config.length; i < len; i++){
5048             if(this.config[i].dataIndex == dataIndex){
5049                 return i;
5050             }
5051         }
5052         return -1;
5053     },
5054     
5055     
5056     moveColumn : function(oldIndex, newIndex){
5057         var c = this.config[oldIndex];
5058         this.config.splice(oldIndex, 1);
5059         this.config.splice(newIndex, 0, c);
5060         this.dataMap = null;
5061         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5062     },
5063
5064     isLocked : function(colIndex){
5065         return this.config[colIndex].locked === true;
5066     },
5067
5068     setLocked : function(colIndex, value, suppressEvent){
5069         if(this.isLocked(colIndex) == value){
5070             return;
5071         }
5072         this.config[colIndex].locked = value;
5073         if(!suppressEvent){
5074             this.fireEvent("columnlockchange", this, colIndex, value);
5075         }
5076     },
5077
5078     getTotalLockedWidth : function(){
5079         var totalWidth = 0;
5080         for(var i = 0; i < this.config.length; i++){
5081             if(this.isLocked(i) && !this.isHidden(i)){
5082                 this.totalWidth += this.getColumnWidth(i);
5083             }
5084         }
5085         return totalWidth;
5086     },
5087
5088     getLockedCount : function(){
5089         for(var i = 0, len = this.config.length; i < len; i++){
5090             if(!this.isLocked(i)){
5091                 return i;
5092             }
5093         }
5094     },
5095
5096     /**
5097      * Returns the number of columns.
5098      * @return {Number}
5099      */
5100     getColumnCount : function(visibleOnly){
5101         if(visibleOnly === true){
5102             var c = 0;
5103             for(var i = 0, len = this.config.length; i < len; i++){
5104                 if(!this.isHidden(i)){
5105                     c++;
5106                 }
5107             }
5108             return c;
5109         }
5110         return this.config.length;
5111     },
5112
5113     /**
5114      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5115      * @param {Function} fn
5116      * @param {Object} scope (optional)
5117      * @return {Array} result
5118      */
5119     getColumnsBy : function(fn, scope){
5120         var r = [];
5121         for(var i = 0, len = this.config.length; i < len; i++){
5122             var c = this.config[i];
5123             if(fn.call(scope||this, c, i) === true){
5124                 r[r.length] = c;
5125             }
5126         }
5127         return r;
5128     },
5129
5130     /**
5131      * Returns true if the specified column is sortable.
5132      * @param {Number} col The column index
5133      * @return {Boolean}
5134      */
5135     isSortable : function(col){
5136         if(typeof this.config[col].sortable == "undefined"){
5137             return this.defaultSortable;
5138         }
5139         return this.config[col].sortable;
5140     },
5141
5142     /**
5143      * Returns the rendering (formatting) function defined for the column.
5144      * @param {Number} col The column index.
5145      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5146      */
5147     getRenderer : function(col){
5148         if(!this.config[col].renderer){
5149             return Roo.grid.ColumnModel.defaultRenderer;
5150         }
5151         return this.config[col].renderer;
5152     },
5153
5154     /**
5155      * Sets the rendering (formatting) function for a column.
5156      * @param {Number} col The column index
5157      * @param {Function} fn The function to use to process the cell's raw data
5158      * to return HTML markup for the grid view. The render function is called with
5159      * the following parameters:<ul>
5160      * <li>Data value.</li>
5161      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5162      * <li>css A CSS style string to apply to the table cell.</li>
5163      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5164      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5165      * <li>Row index</li>
5166      * <li>Column index</li>
5167      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5168      */
5169     setRenderer : function(col, fn){
5170         this.config[col].renderer = fn;
5171     },
5172
5173     /**
5174      * Returns the width for the specified column.
5175      * @param {Number} col The column index
5176      * @return {Number}
5177      */
5178     getColumnWidth : function(col){
5179         return this.config[col].width * 1 || this.defaultWidth;
5180     },
5181
5182     /**
5183      * Sets the width for a column.
5184      * @param {Number} col The column index
5185      * @param {Number} width The new width
5186      */
5187     setColumnWidth : function(col, width, suppressEvent){
5188         this.config[col].width = width;
5189         this.totalWidth = null;
5190         if(!suppressEvent){
5191              this.fireEvent("widthchange", this, col, width);
5192         }
5193     },
5194
5195     /**
5196      * Returns the total width of all columns.
5197      * @param {Boolean} includeHidden True to include hidden column widths
5198      * @return {Number}
5199      */
5200     getTotalWidth : function(includeHidden){
5201         if(!this.totalWidth){
5202             this.totalWidth = 0;
5203             for(var i = 0, len = this.config.length; i < len; i++){
5204                 if(includeHidden || !this.isHidden(i)){
5205                     this.totalWidth += this.getColumnWidth(i);
5206                 }
5207             }
5208         }
5209         return this.totalWidth;
5210     },
5211
5212     /**
5213      * Returns the header for the specified column.
5214      * @param {Number} col The column index
5215      * @return {String}
5216      */
5217     getColumnHeader : function(col){
5218         return this.config[col].header;
5219     },
5220
5221     /**
5222      * Sets the header for a column.
5223      * @param {Number} col The column index
5224      * @param {String} header The new header
5225      */
5226     setColumnHeader : function(col, header){
5227         this.config[col].header = header;
5228         this.fireEvent("headerchange", this, col, header);
5229     },
5230
5231     /**
5232      * Returns the tooltip for the specified column.
5233      * @param {Number} col The column index
5234      * @return {String}
5235      */
5236     getColumnTooltip : function(col){
5237             return this.config[col].tooltip;
5238     },
5239     /**
5240      * Sets the tooltip for a column.
5241      * @param {Number} col The column index
5242      * @param {String} tooltip The new tooltip
5243      */
5244     setColumnTooltip : function(col, tooltip){
5245             this.config[col].tooltip = tooltip;
5246     },
5247
5248     /**
5249      * Returns the dataIndex for the specified column.
5250      * @param {Number} col The column index
5251      * @return {Number}
5252      */
5253     getDataIndex : function(col){
5254         return this.config[col].dataIndex;
5255     },
5256
5257     /**
5258      * Sets the dataIndex for a column.
5259      * @param {Number} col The column index
5260      * @param {Number} dataIndex The new dataIndex
5261      */
5262     setDataIndex : function(col, dataIndex){
5263         this.config[col].dataIndex = dataIndex;
5264     },
5265
5266     
5267     
5268     /**
5269      * Returns true if the cell is editable.
5270      * @param {Number} colIndex The column index
5271      * @param {Number} rowIndex The row index
5272      * @return {Boolean}
5273      */
5274     isCellEditable : function(colIndex, rowIndex){
5275         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5276     },
5277
5278     /**
5279      * Returns the editor defined for the cell/column.
5280      * return false or null to disable editing.
5281      * @param {Number} colIndex The column index
5282      * @param {Number} rowIndex The row index
5283      * @return {Object}
5284      */
5285     getCellEditor : function(colIndex, rowIndex){
5286         return this.config[colIndex].editor;
5287     },
5288
5289     /**
5290      * Sets if a column is editable.
5291      * @param {Number} col The column index
5292      * @param {Boolean} editable True if the column is editable
5293      */
5294     setEditable : function(col, editable){
5295         this.config[col].editable = editable;
5296     },
5297
5298
5299     /**
5300      * Returns true if the column is hidden.
5301      * @param {Number} colIndex The column index
5302      * @return {Boolean}
5303      */
5304     isHidden : function(colIndex){
5305         return this.config[colIndex].hidden;
5306     },
5307
5308
5309     /**
5310      * Returns true if the column width cannot be changed
5311      */
5312     isFixed : function(colIndex){
5313         return this.config[colIndex].fixed;
5314     },
5315
5316     /**
5317      * Returns true if the column can be resized
5318      * @return {Boolean}
5319      */
5320     isResizable : function(colIndex){
5321         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5322     },
5323     /**
5324      * Sets if a column is hidden.
5325      * @param {Number} colIndex The column index
5326      * @param {Boolean} hidden True if the column is hidden
5327      */
5328     setHidden : function(colIndex, hidden){
5329         this.config[colIndex].hidden = hidden;
5330         this.totalWidth = null;
5331         this.fireEvent("hiddenchange", this, colIndex, hidden);
5332     },
5333
5334     /**
5335      * Sets the editor for a column.
5336      * @param {Number} col The column index
5337      * @param {Object} editor The editor object
5338      */
5339     setEditor : function(col, editor){
5340         this.config[col].editor = editor;
5341     }
5342 });
5343
5344 Roo.grid.ColumnModel.defaultRenderer = function(value){
5345         if(typeof value == "string" && value.length < 1){
5346             return "&#160;";
5347         }
5348         return value;
5349 };
5350
5351 // Alias for backwards compatibility
5352 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5353 /*
5354  * Based on:
5355  * Ext JS Library 1.1.1
5356  * Copyright(c) 2006-2007, Ext JS, LLC.
5357  *
5358  * Originally Released Under LGPL - original licence link has changed is not relivant.
5359  *
5360  * Fork - LGPL
5361  * <script type="text/javascript">
5362  */
5363  
5364 /**
5365  * @class Roo.LoadMask
5366  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5367  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5368  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5369  * element's UpdateManager load indicator and will be destroyed after the initial load.
5370  * @constructor
5371  * Create a new LoadMask
5372  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5373  * @param {Object} config The config object
5374  */
5375 Roo.LoadMask = function(el, config){
5376     this.el = Roo.get(el);
5377     Roo.apply(this, config);
5378     if(this.store){
5379         this.store.on('beforeload', this.onBeforeLoad, this);
5380         this.store.on('load', this.onLoad, this);
5381         this.store.on('loadexception', this.onLoadException, this);
5382         this.removeMask = false;
5383     }else{
5384         var um = this.el.getUpdateManager();
5385         um.showLoadIndicator = false; // disable the default indicator
5386         um.on('beforeupdate', this.onBeforeLoad, this);
5387         um.on('update', this.onLoad, this);
5388         um.on('failure', this.onLoad, this);
5389         this.removeMask = true;
5390     }
5391 };
5392
5393 Roo.LoadMask.prototype = {
5394     /**
5395      * @cfg {Boolean} removeMask
5396      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5397      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5398      */
5399     /**
5400      * @cfg {String} msg
5401      * The text to display in a centered loading message box (defaults to 'Loading...')
5402      */
5403     msg : 'Loading...',
5404     /**
5405      * @cfg {String} msgCls
5406      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5407      */
5408     msgCls : 'x-mask-loading',
5409
5410     /**
5411      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5412      * @type Boolean
5413      */
5414     disabled: false,
5415
5416     /**
5417      * Disables the mask to prevent it from being displayed
5418      */
5419     disable : function(){
5420        this.disabled = true;
5421     },
5422
5423     /**
5424      * Enables the mask so that it can be displayed
5425      */
5426     enable : function(){
5427         this.disabled = false;
5428     },
5429     
5430     onLoadException : function()
5431     {
5432         Roo.log(arguments);
5433         
5434         if (typeof(arguments[3]) != 'undefined') {
5435             Roo.MessageBox.alert("Error loading",arguments[3]);
5436         } 
5437         /*
5438         try {
5439             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5440                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5441             }   
5442         } catch(e) {
5443             
5444         }
5445         */
5446     
5447         
5448         
5449         this.el.unmask(this.removeMask);
5450     },
5451     // private
5452     onLoad : function()
5453     {
5454         this.el.unmask(this.removeMask);
5455     },
5456
5457     // private
5458     onBeforeLoad : function(){
5459         if(!this.disabled){
5460             this.el.mask(this.msg, this.msgCls);
5461         }
5462     },
5463
5464     // private
5465     destroy : function(){
5466         if(this.store){
5467             this.store.un('beforeload', this.onBeforeLoad, this);
5468             this.store.un('load', this.onLoad, this);
5469             this.store.un('loadexception', this.onLoadException, this);
5470         }else{
5471             var um = this.el.getUpdateManager();
5472             um.un('beforeupdate', this.onBeforeLoad, this);
5473             um.un('update', this.onLoad, this);
5474             um.un('failure', this.onLoad, this);
5475         }
5476     }
5477 };/*
5478  * - LGPL
5479  *
5480  * table
5481  * 
5482  */
5483
5484 /**
5485  * @class Roo.bootstrap.Table
5486  * @extends Roo.bootstrap.Component
5487  * Bootstrap Table class
5488  * @cfg {String} cls table class
5489  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5490  * @cfg {String} bgcolor Specifies the background color for a table
5491  * @cfg {Number} border Specifies whether the table cells should have borders or not
5492  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5493  * @cfg {Number} cellspacing Specifies the space between cells
5494  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5495  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5496  * @cfg {String} sortable Specifies that the table should be sortable
5497  * @cfg {String} summary Specifies a summary of the content of a table
5498  * @cfg {Number} width Specifies the width of a table
5499  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5500  * 
5501  * @cfg {boolean} striped Should the rows be alternative striped
5502  * @cfg {boolean} bordered Add borders to the table
5503  * @cfg {boolean} hover Add hover highlighting
5504  * @cfg {boolean} condensed Format condensed
5505  * @cfg {boolean} responsive Format condensed
5506  * @cfg {Boolean} loadMask (true|false) default false
5507  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5508  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5509  * @cfg {Boolean} rowSelection (true|false) default false
5510  * @cfg {Boolean} cellSelection (true|false) default false
5511  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5512  
5513  * 
5514  * @constructor
5515  * Create a new Table
5516  * @param {Object} config The config object
5517  */
5518
5519 Roo.bootstrap.Table = function(config){
5520     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5521     
5522     // BC...
5523     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5524     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5525     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5526     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5527     
5528     
5529     if (this.sm) {
5530         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5531         this.sm = this.selModel;
5532         this.sm.xmodule = this.xmodule || false;
5533     }
5534     if (this.cm && typeof(this.cm.config) == 'undefined') {
5535         this.colModel = new Roo.grid.ColumnModel(this.cm);
5536         this.cm = this.colModel;
5537         this.cm.xmodule = this.xmodule || false;
5538     }
5539     if (this.store) {
5540         this.store= Roo.factory(this.store, Roo.data);
5541         this.ds = this.store;
5542         this.ds.xmodule = this.xmodule || false;
5543          
5544     }
5545     if (this.footer && this.store) {
5546         this.footer.dataSource = this.ds;
5547         this.footer = Roo.factory(this.footer);
5548     }
5549     
5550     /** @private */
5551     this.addEvents({
5552         /**
5553          * @event cellclick
5554          * Fires when a cell is clicked
5555          * @param {Roo.bootstrap.Table} this
5556          * @param {Roo.Element} el
5557          * @param {Number} rowIndex
5558          * @param {Number} columnIndex
5559          * @param {Roo.EventObject} e
5560          */
5561         "cellclick" : true,
5562         /**
5563          * @event celldblclick
5564          * Fires when a cell is double clicked
5565          * @param {Roo.bootstrap.Table} this
5566          * @param {Roo.Element} el
5567          * @param {Number} rowIndex
5568          * @param {Number} columnIndex
5569          * @param {Roo.EventObject} e
5570          */
5571         "celldblclick" : true,
5572         /**
5573          * @event rowclick
5574          * Fires when a row is clicked
5575          * @param {Roo.bootstrap.Table} this
5576          * @param {Roo.Element} el
5577          * @param {Number} rowIndex
5578          * @param {Roo.EventObject} e
5579          */
5580         "rowclick" : true,
5581         /**
5582          * @event rowdblclick
5583          * Fires when a row is double clicked
5584          * @param {Roo.bootstrap.Table} this
5585          * @param {Roo.Element} el
5586          * @param {Number} rowIndex
5587          * @param {Roo.EventObject} e
5588          */
5589         "rowdblclick" : true,
5590         /**
5591          * @event mouseover
5592          * Fires when a mouseover occur
5593          * @param {Roo.bootstrap.Table} this
5594          * @param {Roo.Element} el
5595          * @param {Number} rowIndex
5596          * @param {Number} columnIndex
5597          * @param {Roo.EventObject} e
5598          */
5599         "mouseover" : true,
5600         /**
5601          * @event mouseout
5602          * Fires when a mouseout occur
5603          * @param {Roo.bootstrap.Table} this
5604          * @param {Roo.Element} el
5605          * @param {Number} rowIndex
5606          * @param {Number} columnIndex
5607          * @param {Roo.EventObject} e
5608          */
5609         "mouseout" : true,
5610         /**
5611          * @event rowclass
5612          * Fires when a row is rendered, so you can change add a style to it.
5613          * @param {Roo.bootstrap.Table} this
5614          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5615          */
5616         'rowclass' : true,
5617           /**
5618          * @event rowsrendered
5619          * Fires when all the  rows have been rendered
5620          * @param {Roo.bootstrap.Table} this
5621          */
5622         'rowsrendered' : true
5623         
5624     });
5625 };
5626
5627 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5628     
5629     cls: false,
5630     align: false,
5631     bgcolor: false,
5632     border: false,
5633     cellpadding: false,
5634     cellspacing: false,
5635     frame: false,
5636     rules: false,
5637     sortable: false,
5638     summary: false,
5639     width: false,
5640     striped : false,
5641     bordered: false,
5642     hover:  false,
5643     condensed : false,
5644     responsive : false,
5645     sm : false,
5646     cm : false,
5647     store : false,
5648     loadMask : false,
5649     footerShow : true,
5650     headerShow : true,
5651   
5652     rowSelection : false,
5653     cellSelection : false,
5654     layout : false,
5655     
5656     // Roo.Element - the tbody
5657     mainBody: false, 
5658     
5659     getAutoCreate : function(){
5660         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5661         
5662         cfg = {
5663             tag: 'table',
5664             cls : 'table',
5665             cn : []
5666         }
5667             
5668         if (this.striped) {
5669             cfg.cls += ' table-striped';
5670         }
5671         
5672         if (this.hover) {
5673             cfg.cls += ' table-hover';
5674         }
5675         if (this.bordered) {
5676             cfg.cls += ' table-bordered';
5677         }
5678         if (this.condensed) {
5679             cfg.cls += ' table-condensed';
5680         }
5681         if (this.responsive) {
5682             cfg.cls += ' table-responsive';
5683         }
5684         
5685         if (this.cls) {
5686             cfg.cls+=  ' ' +this.cls;
5687         }
5688         
5689         // this lot should be simplifed...
5690         
5691         if (this.align) {
5692             cfg.align=this.align;
5693         }
5694         if (this.bgcolor) {
5695             cfg.bgcolor=this.bgcolor;
5696         }
5697         if (this.border) {
5698             cfg.border=this.border;
5699         }
5700         if (this.cellpadding) {
5701             cfg.cellpadding=this.cellpadding;
5702         }
5703         if (this.cellspacing) {
5704             cfg.cellspacing=this.cellspacing;
5705         }
5706         if (this.frame) {
5707             cfg.frame=this.frame;
5708         }
5709         if (this.rules) {
5710             cfg.rules=this.rules;
5711         }
5712         if (this.sortable) {
5713             cfg.sortable=this.sortable;
5714         }
5715         if (this.summary) {
5716             cfg.summary=this.summary;
5717         }
5718         if (this.width) {
5719             cfg.width=this.width;
5720         }
5721         if (this.layout) {
5722             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5723         }
5724         
5725         if(this.store || this.cm){
5726             if(this.headerShow){
5727                 cfg.cn.push(this.renderHeader());
5728             }
5729             
5730             cfg.cn.push(this.renderBody());
5731             
5732             if(this.footerShow){
5733                 cfg.cn.push(this.renderFooter());
5734             }
5735             
5736             cfg.cls+=  ' TableGrid';
5737         }
5738         
5739         return { cn : [ cfg ] };
5740     },
5741     
5742     initEvents : function()
5743     {   
5744         if(!this.store || !this.cm){
5745             return;
5746         }
5747         
5748         //Roo.log('initEvents with ds!!!!');
5749         
5750         this.mainBody = this.el.select('tbody', true).first();
5751         
5752         
5753         var _this = this;
5754         
5755         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5756             e.on('click', _this.sort, _this);
5757         });
5758         
5759         this.el.on("click", this.onClick, this);
5760         this.el.on("dblclick", this.onDblClick, this);
5761         
5762         // why is this done????? = it breaks dialogs??
5763         //this.parent().el.setStyle('position', 'relative');
5764         
5765         
5766         if (this.footer) {
5767             this.footer.parentId = this.id;
5768             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5769         }
5770         
5771         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5772         
5773         this.store.on('load', this.onLoad, this);
5774         this.store.on('beforeload', this.onBeforeLoad, this);
5775         this.store.on('update', this.onUpdate, this);
5776         this.store.on('add', this.onAdd, this);
5777         
5778     },
5779     
5780     onMouseover : function(e, el)
5781     {
5782         var cell = Roo.get(el);
5783         
5784         if(!cell){
5785             return;
5786         }
5787         
5788         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5789             cell = cell.findParent('td', false, true);
5790         }
5791         
5792         var row = cell.findParent('tr', false, true);
5793         var cellIndex = cell.dom.cellIndex;
5794         var rowIndex = row.dom.rowIndex - 1; // start from 0
5795         
5796         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5797         
5798     },
5799     
5800     onMouseout : function(e, el)
5801     {
5802         var cell = Roo.get(el);
5803         
5804         if(!cell){
5805             return;
5806         }
5807         
5808         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5809             cell = cell.findParent('td', false, true);
5810         }
5811         
5812         var row = cell.findParent('tr', false, true);
5813         var cellIndex = cell.dom.cellIndex;
5814         var rowIndex = row.dom.rowIndex - 1; // start from 0
5815         
5816         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5817         
5818     },
5819     
5820     onClick : function(e, el)
5821     {
5822         var cell = Roo.get(el);
5823         
5824         if(!cell || (!this.cellSelection && !this.rowSelection)){
5825             return;
5826         }
5827         
5828         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5829             cell = cell.findParent('td', false, true);
5830         }
5831         
5832         if(!cell || typeof(cell) == 'undefined'){
5833             return;
5834         }
5835         
5836         var row = cell.findParent('tr', false, true);
5837         
5838         if(!row || typeof(row) == 'undefined'){
5839             return;
5840         }
5841         
5842         var cellIndex = cell.dom.cellIndex;
5843         var rowIndex = this.getRowIndex(row);
5844         
5845         // why??? - should these not be based on SelectionModel?
5846         if(this.cellSelection){
5847             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5848         }
5849         
5850         if(this.rowSelection){
5851             this.fireEvent('rowclick', this, row, rowIndex, e);
5852         }
5853         
5854         
5855     },
5856     
5857     onDblClick : function(e,el)
5858     {
5859         var cell = Roo.get(el);
5860         
5861         if(!cell || (!this.CellSelection && !this.RowSelection)){
5862             return;
5863         }
5864         
5865         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5866             cell = cell.findParent('td', false, true);
5867         }
5868         
5869         if(!cell || typeof(cell) == 'undefined'){
5870             return;
5871         }
5872         
5873         var row = cell.findParent('tr', false, true);
5874         
5875         if(!row || typeof(row) == 'undefined'){
5876             return;
5877         }
5878         
5879         var cellIndex = cell.dom.cellIndex;
5880         var rowIndex = this.getRowIndex(row);
5881         
5882         if(this.CellSelection){
5883             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5884         }
5885         
5886         if(this.RowSelection){
5887             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5888         }
5889     },
5890     
5891     sort : function(e,el)
5892     {
5893         var col = Roo.get(el);
5894         
5895         if(!col.hasClass('sortable')){
5896             return;
5897         }
5898         
5899         var sort = col.attr('sort');
5900         var dir = 'ASC';
5901         
5902         if(col.hasClass('glyphicon-arrow-up')){
5903             dir = 'DESC';
5904         }
5905         
5906         this.store.sortInfo = {field : sort, direction : dir};
5907         
5908         if (this.footer) {
5909             Roo.log("calling footer first");
5910             this.footer.onClick('first');
5911         } else {
5912         
5913             this.store.load({ params : { start : 0 } });
5914         }
5915     },
5916     
5917     renderHeader : function()
5918     {
5919         var header = {
5920             tag: 'thead',
5921             cn : []
5922         };
5923         
5924         var cm = this.cm;
5925         
5926         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5927             
5928             var config = cm.config[i];
5929             
5930             var c = {
5931                 tag: 'th',
5932                 style : '',
5933                 html: cm.getColumnHeader(i)
5934             };
5935             
5936             var hh = '';
5937             
5938             if(typeof(config.lgHeader) != 'undefined'){
5939                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5940             }
5941             
5942             if(typeof(config.mdHeader) != 'undefined'){
5943                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5944             }
5945             
5946             if(typeof(config.smHeader) != 'undefined'){
5947                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5948             }
5949             
5950             if(typeof(config.xsHeader) != 'undefined'){
5951                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5952             }
5953             
5954             if(hh.length){
5955                 c.html = hh;
5956             }
5957             
5958             if(typeof(config.tooltip) != 'undefined'){
5959                 c.tooltip = config.tooltip;
5960             }
5961             
5962             if(typeof(config.colspan) != 'undefined'){
5963                 c.colspan = config.colspan;
5964             }
5965             
5966             if(typeof(config.hidden) != 'undefined' && config.hidden){
5967                 c.style += ' display:none;';
5968             }
5969             
5970             if(typeof(config.dataIndex) != 'undefined'){
5971                 c.sort = config.dataIndex;
5972             }
5973             
5974             if(typeof(config.sortable) != 'undefined' && config.sortable){
5975                 c.cls = 'sortable';
5976             }
5977             
5978             if(typeof(config.align) != 'undefined' && config.align.length){
5979                 c.style += ' text-align:' + config.align + ';';
5980             }
5981             
5982             if(typeof(config.width) != 'undefined'){
5983                 c.style += ' width:' + config.width + 'px;';
5984             }
5985             
5986             if(typeof(config.cls) != 'undefined'){
5987                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5988             }
5989             
5990             ['xs','sm','md','lg'].map(function(size){
5991                 
5992                 if(typeof(config[size]) == 'undefined'){
5993                     return;
5994                 }
5995                 
5996                 if (!config[size]) { // 0 = hidden
5997                     cfg.cls += ' hidden-' + size;
5998                     return;
5999                 }
6000                 
6001                 cfg.cls += ' col-' + size + '-' + config[size];
6002
6003             });
6004             
6005             header.cn.push(c)
6006         }
6007         
6008         return header;
6009     },
6010     
6011     renderBody : function()
6012     {
6013         var body = {
6014             tag: 'tbody',
6015             cn : [
6016                 {
6017                     tag: 'tr',
6018                     cn : [
6019                         {
6020                             tag : 'td',
6021                             colspan :  this.cm.getColumnCount()
6022                         }
6023                     ]
6024                 }
6025             ]
6026         };
6027         
6028         return body;
6029     },
6030     
6031     renderFooter : function()
6032     {
6033         var footer = {
6034             tag: 'tfoot',
6035             cn : [
6036                 {
6037                     tag: 'tr',
6038                     cn : [
6039                         {
6040                             tag : 'td',
6041                             colspan :  this.cm.getColumnCount()
6042                         }
6043                     ]
6044                 }
6045             ]
6046         };
6047         
6048         return footer;
6049     },
6050     
6051     
6052     
6053     onLoad : function()
6054     {
6055         Roo.log('ds onload');
6056         this.clear();
6057         
6058         var _this = this;
6059         var cm = this.cm;
6060         var ds = this.store;
6061         
6062         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6063             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6064             
6065             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6066                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6067             }
6068             
6069             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6070                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6071             }
6072         });
6073         
6074         var tbody =  this.mainBody;
6075               
6076         if(ds.getCount() > 0){
6077             ds.data.each(function(d,rowIndex){
6078                 var row =  this.renderRow(cm, ds, rowIndex);
6079                 
6080                 tbody.createChild(row);
6081                 
6082                 var _this = this;
6083                 
6084                 if(row.cellObjects.length){
6085                     Roo.each(row.cellObjects, function(r){
6086                         _this.renderCellObject(r);
6087                     })
6088                 }
6089                 
6090             }, this);
6091         }
6092         
6093         Roo.each(this.el.select('tbody td', true).elements, function(e){
6094             e.on('mouseover', _this.onMouseover, _this);
6095         });
6096         
6097         Roo.each(this.el.select('tbody td', true).elements, function(e){
6098             e.on('mouseout', _this.onMouseout, _this);
6099         });
6100         this.fireEvent('rowsrendered', this);
6101         //if(this.loadMask){
6102         //    this.maskEl.hide();
6103         //}
6104     },
6105     
6106     
6107     onUpdate : function(ds,record)
6108     {
6109         this.refreshRow(record);
6110     },
6111     
6112     onRemove : function(ds, record, index, isUpdate){
6113         if(isUpdate !== true){
6114             this.fireEvent("beforerowremoved", this, index, record);
6115         }
6116         var bt = this.mainBody.dom;
6117         
6118         var rows = this.el.select('tbody > tr', true).elements;
6119         
6120         if(typeof(rows[index]) != 'undefined'){
6121             bt.removeChild(rows[index].dom);
6122         }
6123         
6124 //        if(bt.rows[index]){
6125 //            bt.removeChild(bt.rows[index]);
6126 //        }
6127         
6128         if(isUpdate !== true){
6129             //this.stripeRows(index);
6130             //this.syncRowHeights(index, index);
6131             //this.layout();
6132             this.fireEvent("rowremoved", this, index, record);
6133         }
6134     },
6135     
6136     onAdd : function(ds, records, rowIndex)
6137     {
6138         //Roo.log('on Add called');
6139         // - note this does not handle multiple adding very well..
6140         var bt = this.mainBody.dom;
6141         for (var i =0 ; i < records.length;i++) {
6142             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6143             //Roo.log(records[i]);
6144             //Roo.log(this.store.getAt(rowIndex+i));
6145             this.insertRow(this.store, rowIndex + i, false);
6146             return;
6147         }
6148         
6149     },
6150     
6151     
6152     refreshRow : function(record){
6153         var ds = this.store, index;
6154         if(typeof record == 'number'){
6155             index = record;
6156             record = ds.getAt(index);
6157         }else{
6158             index = ds.indexOf(record);
6159         }
6160         this.insertRow(ds, index, true);
6161         this.onRemove(ds, record, index+1, true);
6162         //this.syncRowHeights(index, index);
6163         //this.layout();
6164         this.fireEvent("rowupdated", this, index, record);
6165     },
6166     
6167     insertRow : function(dm, rowIndex, isUpdate){
6168         
6169         if(!isUpdate){
6170             this.fireEvent("beforerowsinserted", this, rowIndex);
6171         }
6172             //var s = this.getScrollState();
6173         var row = this.renderRow(this.cm, this.store, rowIndex);
6174         // insert before rowIndex..
6175         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6176         
6177         var _this = this;
6178                 
6179         if(row.cellObjects.length){
6180             Roo.each(row.cellObjects, function(r){
6181                 _this.renderCellObject(r);
6182             })
6183         }
6184             
6185         if(!isUpdate){
6186             this.fireEvent("rowsinserted", this, rowIndex);
6187             //this.syncRowHeights(firstRow, lastRow);
6188             //this.stripeRows(firstRow);
6189             //this.layout();
6190         }
6191         
6192     },
6193     
6194     
6195     getRowDom : function(rowIndex)
6196     {
6197         var rows = this.el.select('tbody > tr', true).elements;
6198         
6199         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6200         
6201     },
6202     // returns the object tree for a tr..
6203   
6204     
6205     renderRow : function(cm, ds, rowIndex) 
6206     {
6207         
6208         var d = ds.getAt(rowIndex);
6209         
6210         var row = {
6211             tag : 'tr',
6212             cn : []
6213         };
6214             
6215         var cellObjects = [];
6216         
6217         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6218             var config = cm.config[i];
6219             
6220             var renderer = cm.getRenderer(i);
6221             var value = '';
6222             var id = false;
6223             
6224             if(typeof(renderer) !== 'undefined'){
6225                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6226             }
6227             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6228             // and are rendered into the cells after the row is rendered - using the id for the element.
6229             
6230             if(typeof(value) === 'object'){
6231                 id = Roo.id();
6232                 cellObjects.push({
6233                     container : id,
6234                     cfg : value 
6235                 })
6236             }
6237             
6238             var rowcfg = {
6239                 record: d,
6240                 rowIndex : rowIndex,
6241                 colIndex : i,
6242                 rowClass : ''
6243             }
6244
6245             this.fireEvent('rowclass', this, rowcfg);
6246             
6247             var td = {
6248                 tag: 'td',
6249                 cls : rowcfg.rowClass,
6250                 style: '',
6251                 html: (typeof(value) === 'object') ? '' : value
6252             };
6253             
6254             if (id) {
6255                 td.id = id;
6256             }
6257             
6258             if(typeof(config.colspan) != 'undefined'){
6259                 td.colspan = config.colspan;
6260             }
6261             
6262             if(typeof(config.hidden) != 'undefined' && config.hidden){
6263                 td.style += ' display:none;';
6264             }
6265             
6266             if(typeof(config.align) != 'undefined' && config.align.length){
6267                 td.style += ' text-align:' + config.align + ';';
6268             }
6269             
6270             if(typeof(config.width) != 'undefined'){
6271                 td.style += ' width:' +  config.width + 'px;';
6272             }
6273             
6274             if(typeof(config.cursor) != 'undefined'){
6275                 td.style += ' cursor:' +  config.cursor + ';';
6276             }
6277             
6278             if(typeof(config.cls) != 'undefined'){
6279                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6280             }
6281             
6282             ['xs','sm','md','lg'].map(function(size){
6283                 
6284                 if(typeof(config[size]) == 'undefined'){
6285                     return;
6286                 }
6287                 
6288                 if (!config[size]) { // 0 = hidden
6289                     td.cls += ' hidden-' + size;
6290                     return;
6291                 }
6292                 
6293                 td.cls += ' col-' + size + '-' + config[size];
6294
6295             });
6296              
6297             row.cn.push(td);
6298            
6299         }
6300         
6301         row.cellObjects = cellObjects;
6302         
6303         return row;
6304           
6305     },
6306     
6307     
6308     
6309     onBeforeLoad : function()
6310     {
6311         //Roo.log('ds onBeforeLoad');
6312         
6313         //this.clear();
6314         
6315         //if(this.loadMask){
6316         //    this.maskEl.show();
6317         //}
6318     },
6319      /**
6320      * Remove all rows
6321      */
6322     clear : function()
6323     {
6324         this.el.select('tbody', true).first().dom.innerHTML = '';
6325     },
6326     /**
6327      * Show or hide a row.
6328      * @param {Number} rowIndex to show or hide
6329      * @param {Boolean} state hide
6330      */
6331     setRowVisibility : function(rowIndex, state)
6332     {
6333         var bt = this.mainBody.dom;
6334         
6335         var rows = this.el.select('tbody > tr', true).elements;
6336         
6337         if(typeof(rows[rowIndex]) == 'undefined'){
6338             return;
6339         }
6340         rows[rowIndex].dom.style.display = state ? '' : 'none';
6341     },
6342     
6343     
6344     getSelectionModel : function(){
6345         if(!this.selModel){
6346             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6347         }
6348         return this.selModel;
6349     },
6350     /*
6351      * Render the Roo.bootstrap object from renderder
6352      */
6353     renderCellObject : function(r)
6354     {
6355         var _this = this;
6356         
6357         var t = r.cfg.render(r.container);
6358         
6359         if(r.cfg.cn){
6360             Roo.each(r.cfg.cn, function(c){
6361                 var child = {
6362                     container: t.getChildContainer(),
6363                     cfg: c
6364                 }
6365                 _this.renderCellObject(child);
6366             })
6367         }
6368     },
6369     
6370     getRowIndex : function(row)
6371     {
6372         var rowIndex = -1;
6373         
6374         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6375             if(el != row){
6376                 return;
6377             }
6378             
6379             rowIndex = index;
6380         });
6381         
6382         return rowIndex;
6383     }
6384    
6385 });
6386
6387  
6388
6389  /*
6390  * - LGPL
6391  *
6392  * table cell
6393  * 
6394  */
6395
6396 /**
6397  * @class Roo.bootstrap.TableCell
6398  * @extends Roo.bootstrap.Component
6399  * Bootstrap TableCell class
6400  * @cfg {String} html cell contain text
6401  * @cfg {String} cls cell class
6402  * @cfg {String} tag cell tag (td|th) default td
6403  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6404  * @cfg {String} align Aligns the content in a cell
6405  * @cfg {String} axis Categorizes cells
6406  * @cfg {String} bgcolor Specifies the background color of a cell
6407  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6408  * @cfg {Number} colspan Specifies the number of columns a cell should span
6409  * @cfg {String} headers Specifies one or more header cells a cell is related to
6410  * @cfg {Number} height Sets the height of a cell
6411  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6412  * @cfg {Number} rowspan Sets the number of rows a cell should span
6413  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6414  * @cfg {String} valign Vertical aligns the content in a cell
6415  * @cfg {Number} width Specifies the width of a cell
6416  * 
6417  * @constructor
6418  * Create a new TableCell
6419  * @param {Object} config The config object
6420  */
6421
6422 Roo.bootstrap.TableCell = function(config){
6423     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6424 };
6425
6426 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6427     
6428     html: false,
6429     cls: false,
6430     tag: false,
6431     abbr: false,
6432     align: false,
6433     axis: false,
6434     bgcolor: false,
6435     charoff: false,
6436     colspan: false,
6437     headers: false,
6438     height: false,
6439     nowrap: false,
6440     rowspan: false,
6441     scope: false,
6442     valign: false,
6443     width: false,
6444     
6445     
6446     getAutoCreate : function(){
6447         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6448         
6449         cfg = {
6450             tag: 'td'
6451         }
6452         
6453         if(this.tag){
6454             cfg.tag = this.tag;
6455         }
6456         
6457         if (this.html) {
6458             cfg.html=this.html
6459         }
6460         if (this.cls) {
6461             cfg.cls=this.cls
6462         }
6463         if (this.abbr) {
6464             cfg.abbr=this.abbr
6465         }
6466         if (this.align) {
6467             cfg.align=this.align
6468         }
6469         if (this.axis) {
6470             cfg.axis=this.axis
6471         }
6472         if (this.bgcolor) {
6473             cfg.bgcolor=this.bgcolor
6474         }
6475         if (this.charoff) {
6476             cfg.charoff=this.charoff
6477         }
6478         if (this.colspan) {
6479             cfg.colspan=this.colspan
6480         }
6481         if (this.headers) {
6482             cfg.headers=this.headers
6483         }
6484         if (this.height) {
6485             cfg.height=this.height
6486         }
6487         if (this.nowrap) {
6488             cfg.nowrap=this.nowrap
6489         }
6490         if (this.rowspan) {
6491             cfg.rowspan=this.rowspan
6492         }
6493         if (this.scope) {
6494             cfg.scope=this.scope
6495         }
6496         if (this.valign) {
6497             cfg.valign=this.valign
6498         }
6499         if (this.width) {
6500             cfg.width=this.width
6501         }
6502         
6503         
6504         return cfg;
6505     }
6506    
6507 });
6508
6509  
6510
6511  /*
6512  * - LGPL
6513  *
6514  * table row
6515  * 
6516  */
6517
6518 /**
6519  * @class Roo.bootstrap.TableRow
6520  * @extends Roo.bootstrap.Component
6521  * Bootstrap TableRow class
6522  * @cfg {String} cls row class
6523  * @cfg {String} align Aligns the content in a table row
6524  * @cfg {String} bgcolor Specifies a background color for a table row
6525  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6526  * @cfg {String} valign Vertical aligns the content in a table row
6527  * 
6528  * @constructor
6529  * Create a new TableRow
6530  * @param {Object} config The config object
6531  */
6532
6533 Roo.bootstrap.TableRow = function(config){
6534     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6535 };
6536
6537 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6538     
6539     cls: false,
6540     align: false,
6541     bgcolor: false,
6542     charoff: false,
6543     valign: false,
6544     
6545     getAutoCreate : function(){
6546         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6547         
6548         cfg = {
6549             tag: 'tr'
6550         }
6551             
6552         if(this.cls){
6553             cfg.cls = this.cls;
6554         }
6555         if(this.align){
6556             cfg.align = this.align;
6557         }
6558         if(this.bgcolor){
6559             cfg.bgcolor = this.bgcolor;
6560         }
6561         if(this.charoff){
6562             cfg.charoff = this.charoff;
6563         }
6564         if(this.valign){
6565             cfg.valign = this.valign;
6566         }
6567         
6568         return cfg;
6569     }
6570    
6571 });
6572
6573  
6574
6575  /*
6576  * - LGPL
6577  *
6578  * table body
6579  * 
6580  */
6581
6582 /**
6583  * @class Roo.bootstrap.TableBody
6584  * @extends Roo.bootstrap.Component
6585  * Bootstrap TableBody class
6586  * @cfg {String} cls element class
6587  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6588  * @cfg {String} align Aligns the content inside the element
6589  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6590  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6591  * 
6592  * @constructor
6593  * Create a new TableBody
6594  * @param {Object} config The config object
6595  */
6596
6597 Roo.bootstrap.TableBody = function(config){
6598     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6599 };
6600
6601 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6602     
6603     cls: false,
6604     tag: false,
6605     align: false,
6606     charoff: false,
6607     valign: false,
6608     
6609     getAutoCreate : function(){
6610         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6611         
6612         cfg = {
6613             tag: 'tbody'
6614         }
6615             
6616         if (this.cls) {
6617             cfg.cls=this.cls
6618         }
6619         if(this.tag){
6620             cfg.tag = this.tag;
6621         }
6622         
6623         if(this.align){
6624             cfg.align = this.align;
6625         }
6626         if(this.charoff){
6627             cfg.charoff = this.charoff;
6628         }
6629         if(this.valign){
6630             cfg.valign = this.valign;
6631         }
6632         
6633         return cfg;
6634     }
6635     
6636     
6637 //    initEvents : function()
6638 //    {
6639 //        
6640 //        if(!this.store){
6641 //            return;
6642 //        }
6643 //        
6644 //        this.store = Roo.factory(this.store, Roo.data);
6645 //        this.store.on('load', this.onLoad, this);
6646 //        
6647 //        this.store.load();
6648 //        
6649 //    },
6650 //    
6651 //    onLoad: function () 
6652 //    {   
6653 //        this.fireEvent('load', this);
6654 //    }
6655 //    
6656 //   
6657 });
6658
6659  
6660
6661  /*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 // as we use this in bootstrap.
6673 Roo.namespace('Roo.form');
6674  /**
6675  * @class Roo.form.Action
6676  * Internal Class used to handle form actions
6677  * @constructor
6678  * @param {Roo.form.BasicForm} el The form element or its id
6679  * @param {Object} config Configuration options
6680  */
6681
6682  
6683  
6684 // define the action interface
6685 Roo.form.Action = function(form, options){
6686     this.form = form;
6687     this.options = options || {};
6688 };
6689 /**
6690  * Client Validation Failed
6691  * @const 
6692  */
6693 Roo.form.Action.CLIENT_INVALID = 'client';
6694 /**
6695  * Server Validation Failed
6696  * @const 
6697  */
6698 Roo.form.Action.SERVER_INVALID = 'server';
6699  /**
6700  * Connect to Server Failed
6701  * @const 
6702  */
6703 Roo.form.Action.CONNECT_FAILURE = 'connect';
6704 /**
6705  * Reading Data from Server Failed
6706  * @const 
6707  */
6708 Roo.form.Action.LOAD_FAILURE = 'load';
6709
6710 Roo.form.Action.prototype = {
6711     type : 'default',
6712     failureType : undefined,
6713     response : undefined,
6714     result : undefined,
6715
6716     // interface method
6717     run : function(options){
6718
6719     },
6720
6721     // interface method
6722     success : function(response){
6723
6724     },
6725
6726     // interface method
6727     handleResponse : function(response){
6728
6729     },
6730
6731     // default connection failure
6732     failure : function(response){
6733         
6734         this.response = response;
6735         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6736         this.form.afterAction(this, false);
6737     },
6738
6739     processResponse : function(response){
6740         this.response = response;
6741         if(!response.responseText){
6742             return true;
6743         }
6744         this.result = this.handleResponse(response);
6745         return this.result;
6746     },
6747
6748     // utility functions used internally
6749     getUrl : function(appendParams){
6750         var url = this.options.url || this.form.url || this.form.el.dom.action;
6751         if(appendParams){
6752             var p = this.getParams();
6753             if(p){
6754                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6755             }
6756         }
6757         return url;
6758     },
6759
6760     getMethod : function(){
6761         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6762     },
6763
6764     getParams : function(){
6765         var bp = this.form.baseParams;
6766         var p = this.options.params;
6767         if(p){
6768             if(typeof p == "object"){
6769                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6770             }else if(typeof p == 'string' && bp){
6771                 p += '&' + Roo.urlEncode(bp);
6772             }
6773         }else if(bp){
6774             p = Roo.urlEncode(bp);
6775         }
6776         return p;
6777     },
6778
6779     createCallback : function(){
6780         return {
6781             success: this.success,
6782             failure: this.failure,
6783             scope: this,
6784             timeout: (this.form.timeout*1000),
6785             upload: this.form.fileUpload ? this.success : undefined
6786         };
6787     }
6788 };
6789
6790 Roo.form.Action.Submit = function(form, options){
6791     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6792 };
6793
6794 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6795     type : 'submit',
6796
6797     haveProgress : false,
6798     uploadComplete : false,
6799     
6800     // uploadProgress indicator.
6801     uploadProgress : function()
6802     {
6803         if (!this.form.progressUrl) {
6804             return;
6805         }
6806         
6807         if (!this.haveProgress) {
6808             Roo.MessageBox.progress("Uploading", "Uploading");
6809         }
6810         if (this.uploadComplete) {
6811            Roo.MessageBox.hide();
6812            return;
6813         }
6814         
6815         this.haveProgress = true;
6816    
6817         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6818         
6819         var c = new Roo.data.Connection();
6820         c.request({
6821             url : this.form.progressUrl,
6822             params: {
6823                 id : uid
6824             },
6825             method: 'GET',
6826             success : function(req){
6827                //console.log(data);
6828                 var rdata = false;
6829                 var edata;
6830                 try  {
6831                    rdata = Roo.decode(req.responseText)
6832                 } catch (e) {
6833                     Roo.log("Invalid data from server..");
6834                     Roo.log(edata);
6835                     return;
6836                 }
6837                 if (!rdata || !rdata.success) {
6838                     Roo.log(rdata);
6839                     Roo.MessageBox.alert(Roo.encode(rdata));
6840                     return;
6841                 }
6842                 var data = rdata.data;
6843                 
6844                 if (this.uploadComplete) {
6845                    Roo.MessageBox.hide();
6846                    return;
6847                 }
6848                    
6849                 if (data){
6850                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6851                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6852                     );
6853                 }
6854                 this.uploadProgress.defer(2000,this);
6855             },
6856        
6857             failure: function(data) {
6858                 Roo.log('progress url failed ');
6859                 Roo.log(data);
6860             },
6861             scope : this
6862         });
6863            
6864     },
6865     
6866     
6867     run : function()
6868     {
6869         // run get Values on the form, so it syncs any secondary forms.
6870         this.form.getValues();
6871         
6872         var o = this.options;
6873         var method = this.getMethod();
6874         var isPost = method == 'POST';
6875         if(o.clientValidation === false || this.form.isValid()){
6876             
6877             if (this.form.progressUrl) {
6878                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6879                     (new Date() * 1) + '' + Math.random());
6880                     
6881             } 
6882             
6883             
6884             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6885                 form:this.form.el.dom,
6886                 url:this.getUrl(!isPost),
6887                 method: method,
6888                 params:isPost ? this.getParams() : null,
6889                 isUpload: this.form.fileUpload
6890             }));
6891             
6892             this.uploadProgress();
6893
6894         }else if (o.clientValidation !== false){ // client validation failed
6895             this.failureType = Roo.form.Action.CLIENT_INVALID;
6896             this.form.afterAction(this, false);
6897         }
6898     },
6899
6900     success : function(response)
6901     {
6902         this.uploadComplete= true;
6903         if (this.haveProgress) {
6904             Roo.MessageBox.hide();
6905         }
6906         
6907         
6908         var result = this.processResponse(response);
6909         if(result === true || result.success){
6910             this.form.afterAction(this, true);
6911             return;
6912         }
6913         if(result.errors){
6914             this.form.markInvalid(result.errors);
6915             this.failureType = Roo.form.Action.SERVER_INVALID;
6916         }
6917         this.form.afterAction(this, false);
6918     },
6919     failure : function(response)
6920     {
6921         this.uploadComplete= true;
6922         if (this.haveProgress) {
6923             Roo.MessageBox.hide();
6924         }
6925         
6926         this.response = response;
6927         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6928         this.form.afterAction(this, false);
6929     },
6930     
6931     handleResponse : function(response){
6932         if(this.form.errorReader){
6933             var rs = this.form.errorReader.read(response);
6934             var errors = [];
6935             if(rs.records){
6936                 for(var i = 0, len = rs.records.length; i < len; i++) {
6937                     var r = rs.records[i];
6938                     errors[i] = r.data;
6939                 }
6940             }
6941             if(errors.length < 1){
6942                 errors = null;
6943             }
6944             return {
6945                 success : rs.success,
6946                 errors : errors
6947             };
6948         }
6949         var ret = false;
6950         try {
6951             ret = Roo.decode(response.responseText);
6952         } catch (e) {
6953             ret = {
6954                 success: false,
6955                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6956                 errors : []
6957             };
6958         }
6959         return ret;
6960         
6961     }
6962 });
6963
6964
6965 Roo.form.Action.Load = function(form, options){
6966     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6967     this.reader = this.form.reader;
6968 };
6969
6970 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6971     type : 'load',
6972
6973     run : function(){
6974         
6975         Roo.Ajax.request(Roo.apply(
6976                 this.createCallback(), {
6977                     method:this.getMethod(),
6978                     url:this.getUrl(false),
6979                     params:this.getParams()
6980         }));
6981     },
6982
6983     success : function(response){
6984         
6985         var result = this.processResponse(response);
6986         if(result === true || !result.success || !result.data){
6987             this.failureType = Roo.form.Action.LOAD_FAILURE;
6988             this.form.afterAction(this, false);
6989             return;
6990         }
6991         this.form.clearInvalid();
6992         this.form.setValues(result.data);
6993         this.form.afterAction(this, true);
6994     },
6995
6996     handleResponse : function(response){
6997         if(this.form.reader){
6998             var rs = this.form.reader.read(response);
6999             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7000             return {
7001                 success : rs.success,
7002                 data : data
7003             };
7004         }
7005         return Roo.decode(response.responseText);
7006     }
7007 });
7008
7009 Roo.form.Action.ACTION_TYPES = {
7010     'load' : Roo.form.Action.Load,
7011     'submit' : Roo.form.Action.Submit
7012 };/*
7013  * - LGPL
7014  *
7015  * form
7016  * 
7017  */
7018
7019 /**
7020  * @class Roo.bootstrap.Form
7021  * @extends Roo.bootstrap.Component
7022  * Bootstrap Form class
7023  * @cfg {String} method  GET | POST (default POST)
7024  * @cfg {String} labelAlign top | left (default top)
7025  * @cfg {String} align left  | right - for navbars
7026  * @cfg {Boolean} loadMask load mask when submit (default true)
7027
7028  * 
7029  * @constructor
7030  * Create a new Form
7031  * @param {Object} config The config object
7032  */
7033
7034
7035 Roo.bootstrap.Form = function(config){
7036     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7037     this.addEvents({
7038         /**
7039          * @event clientvalidation
7040          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7041          * @param {Form} this
7042          * @param {Boolean} valid true if the form has passed client-side validation
7043          */
7044         clientvalidation: true,
7045         /**
7046          * @event beforeaction
7047          * Fires before any action is performed. Return false to cancel the action.
7048          * @param {Form} this
7049          * @param {Action} action The action to be performed
7050          */
7051         beforeaction: true,
7052         /**
7053          * @event actionfailed
7054          * Fires when an action fails.
7055          * @param {Form} this
7056          * @param {Action} action The action that failed
7057          */
7058         actionfailed : true,
7059         /**
7060          * @event actioncomplete
7061          * Fires when an action is completed.
7062          * @param {Form} this
7063          * @param {Action} action The action that completed
7064          */
7065         actioncomplete : true
7066     });
7067     
7068 };
7069
7070 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7071       
7072      /**
7073      * @cfg {String} method
7074      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7075      */
7076     method : 'POST',
7077     /**
7078      * @cfg {String} url
7079      * The URL to use for form actions if one isn't supplied in the action options.
7080      */
7081     /**
7082      * @cfg {Boolean} fileUpload
7083      * Set to true if this form is a file upload.
7084      */
7085      
7086     /**
7087      * @cfg {Object} baseParams
7088      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7089      */
7090       
7091     /**
7092      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7093      */
7094     timeout: 30,
7095     /**
7096      * @cfg {Sting} align (left|right) for navbar forms
7097      */
7098     align : 'left',
7099
7100     // private
7101     activeAction : null,
7102  
7103     /**
7104      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7105      * element by passing it or its id or mask the form itself by passing in true.
7106      * @type Mixed
7107      */
7108     waitMsgTarget : false,
7109     
7110     loadMask : true,
7111     
7112     getAutoCreate : function(){
7113         
7114         var cfg = {
7115             tag: 'form',
7116             method : this.method || 'POST',
7117             id : this.id || Roo.id(),
7118             cls : ''
7119         }
7120         if (this.parent().xtype.match(/^Nav/)) {
7121             cfg.cls = 'navbar-form navbar-' + this.align;
7122             
7123         }
7124         
7125         if (this.labelAlign == 'left' ) {
7126             cfg.cls += ' form-horizontal';
7127         }
7128         
7129         
7130         return cfg;
7131     },
7132     initEvents : function()
7133     {
7134         this.el.on('submit', this.onSubmit, this);
7135         // this was added as random key presses on the form where triggering form submit.
7136         this.el.on('keypress', function(e) {
7137             if (e.getCharCode() != 13) {
7138                 return true;
7139             }
7140             // we might need to allow it for textareas.. and some other items.
7141             // check e.getTarget().
7142             
7143             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7144                 return true;
7145             }
7146         
7147             Roo.log("keypress blocked");
7148             
7149             e.preventDefault();
7150             return false;
7151         });
7152         
7153     },
7154     // private
7155     onSubmit : function(e){
7156         e.stopEvent();
7157     },
7158     
7159      /**
7160      * Returns true if client-side validation on the form is successful.
7161      * @return Boolean
7162      */
7163     isValid : function(){
7164         var items = this.getItems();
7165         var valid = true;
7166         items.each(function(f){
7167            if(!f.validate()){
7168                valid = false;
7169                
7170            }
7171         });
7172         return valid;
7173     },
7174     /**
7175      * Returns true if any fields in this form have changed since their original load.
7176      * @return Boolean
7177      */
7178     isDirty : function(){
7179         var dirty = false;
7180         var items = this.getItems();
7181         items.each(function(f){
7182            if(f.isDirty()){
7183                dirty = true;
7184                return false;
7185            }
7186            return true;
7187         });
7188         return dirty;
7189     },
7190      /**
7191      * Performs a predefined action (submit or load) or custom actions you define on this form.
7192      * @param {String} actionName The name of the action type
7193      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7194      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7195      * accept other config options):
7196      * <pre>
7197 Property          Type             Description
7198 ----------------  ---------------  ----------------------------------------------------------------------------------
7199 url               String           The url for the action (defaults to the form's url)
7200 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7201 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7202 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7203                                    validate the form on the client (defaults to false)
7204      * </pre>
7205      * @return {BasicForm} this
7206      */
7207     doAction : function(action, options){
7208         if(typeof action == 'string'){
7209             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7210         }
7211         if(this.fireEvent('beforeaction', this, action) !== false){
7212             this.beforeAction(action);
7213             action.run.defer(100, action);
7214         }
7215         return this;
7216     },
7217     
7218     // private
7219     beforeAction : function(action){
7220         var o = action.options;
7221         
7222         if(this.loadMask){
7223             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7224         }
7225         // not really supported yet.. ??
7226         
7227         //if(this.waitMsgTarget === true){
7228         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7229         //}else if(this.waitMsgTarget){
7230         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7231         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7232         //}else {
7233         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7234        // }
7235          
7236     },
7237
7238     // private
7239     afterAction : function(action, success){
7240         this.activeAction = null;
7241         var o = action.options;
7242         
7243         //if(this.waitMsgTarget === true){
7244             this.el.unmask();
7245         //}else if(this.waitMsgTarget){
7246         //    this.waitMsgTarget.unmask();
7247         //}else{
7248         //    Roo.MessageBox.updateProgress(1);
7249         //    Roo.MessageBox.hide();
7250        // }
7251         // 
7252         if(success){
7253             if(o.reset){
7254                 this.reset();
7255             }
7256             Roo.callback(o.success, o.scope, [this, action]);
7257             this.fireEvent('actioncomplete', this, action);
7258             
7259         }else{
7260             
7261             // failure condition..
7262             // we have a scenario where updates need confirming.
7263             // eg. if a locking scenario exists..
7264             // we look for { errors : { needs_confirm : true }} in the response.
7265             if (
7266                 (typeof(action.result) != 'undefined')  &&
7267                 (typeof(action.result.errors) != 'undefined')  &&
7268                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7269            ){
7270                 var _t = this;
7271                 Roo.log("not supported yet");
7272                  /*
7273                 
7274                 Roo.MessageBox.confirm(
7275                     "Change requires confirmation",
7276                     action.result.errorMsg,
7277                     function(r) {
7278                         if (r != 'yes') {
7279                             return;
7280                         }
7281                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7282                     }
7283                     
7284                 );
7285                 */
7286                 
7287                 
7288                 return;
7289             }
7290             
7291             Roo.callback(o.failure, o.scope, [this, action]);
7292             // show an error message if no failed handler is set..
7293             if (!this.hasListener('actionfailed')) {
7294                 Roo.log("need to add dialog support");
7295                 /*
7296                 Roo.MessageBox.alert("Error",
7297                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7298                         action.result.errorMsg :
7299                         "Saving Failed, please check your entries or try again"
7300                 );
7301                 */
7302             }
7303             
7304             this.fireEvent('actionfailed', this, action);
7305         }
7306         
7307     },
7308     /**
7309      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7310      * @param {String} id The value to search for
7311      * @return Field
7312      */
7313     findField : function(id){
7314         var items = this.getItems();
7315         var field = items.get(id);
7316         if(!field){
7317              items.each(function(f){
7318                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7319                     field = f;
7320                     return false;
7321                 }
7322                 return true;
7323             });
7324         }
7325         return field || null;
7326     },
7327      /**
7328      * Mark fields in this form invalid in bulk.
7329      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7330      * @return {BasicForm} this
7331      */
7332     markInvalid : function(errors){
7333         if(errors instanceof Array){
7334             for(var i = 0, len = errors.length; i < len; i++){
7335                 var fieldError = errors[i];
7336                 var f = this.findField(fieldError.id);
7337                 if(f){
7338                     f.markInvalid(fieldError.msg);
7339                 }
7340             }
7341         }else{
7342             var field, id;
7343             for(id in errors){
7344                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7345                     field.markInvalid(errors[id]);
7346                 }
7347             }
7348         }
7349         //Roo.each(this.childForms || [], function (f) {
7350         //    f.markInvalid(errors);
7351         //});
7352         
7353         return this;
7354     },
7355
7356     /**
7357      * Set values for fields in this form in bulk.
7358      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7359      * @return {BasicForm} this
7360      */
7361     setValues : function(values){
7362         if(values instanceof Array){ // array of objects
7363             for(var i = 0, len = values.length; i < len; i++){
7364                 var v = values[i];
7365                 var f = this.findField(v.id);
7366                 if(f){
7367                     f.setValue(v.value);
7368                     if(this.trackResetOnLoad){
7369                         f.originalValue = f.getValue();
7370                     }
7371                 }
7372             }
7373         }else{ // object hash
7374             var field, id;
7375             for(id in values){
7376                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7377                     
7378                     if (field.setFromData && 
7379                         field.valueField && 
7380                         field.displayField &&
7381                         // combos' with local stores can 
7382                         // be queried via setValue()
7383                         // to set their value..
7384                         (field.store && !field.store.isLocal)
7385                         ) {
7386                         // it's a combo
7387                         var sd = { };
7388                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7389                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7390                         field.setFromData(sd);
7391                         
7392                     } else {
7393                         field.setValue(values[id]);
7394                     }
7395                     
7396                     
7397                     if(this.trackResetOnLoad){
7398                         field.originalValue = field.getValue();
7399                     }
7400                 }
7401             }
7402         }
7403          
7404         //Roo.each(this.childForms || [], function (f) {
7405         //    f.setValues(values);
7406         //});
7407                 
7408         return this;
7409     },
7410
7411     /**
7412      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7413      * they are returned as an array.
7414      * @param {Boolean} asString
7415      * @return {Object}
7416      */
7417     getValues : function(asString){
7418         //if (this.childForms) {
7419             // copy values from the child forms
7420         //    Roo.each(this.childForms, function (f) {
7421         //        this.setValues(f.getValues());
7422         //    }, this);
7423         //}
7424         
7425         
7426         
7427         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7428         if(asString === true){
7429             return fs;
7430         }
7431         return Roo.urlDecode(fs);
7432     },
7433     
7434     /**
7435      * Returns the fields in this form as an object with key/value pairs. 
7436      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7437      * @return {Object}
7438      */
7439     getFieldValues : function(with_hidden)
7440     {
7441         var items = this.getItems();
7442         var ret = {};
7443         items.each(function(f){
7444             if (!f.getName()) {
7445                 return;
7446             }
7447             var v = f.getValue();
7448             if (f.inputType =='radio') {
7449                 if (typeof(ret[f.getName()]) == 'undefined') {
7450                     ret[f.getName()] = ''; // empty..
7451                 }
7452                 
7453                 if (!f.el.dom.checked) {
7454                     return;
7455                     
7456                 }
7457                 v = f.el.dom.value;
7458                 
7459             }
7460             
7461             // not sure if this supported any more..
7462             if ((typeof(v) == 'object') && f.getRawValue) {
7463                 v = f.getRawValue() ; // dates..
7464             }
7465             // combo boxes where name != hiddenName...
7466             if (f.name != f.getName()) {
7467                 ret[f.name] = f.getRawValue();
7468             }
7469             ret[f.getName()] = v;
7470         });
7471         
7472         return ret;
7473     },
7474
7475     /**
7476      * Clears all invalid messages in this form.
7477      * @return {BasicForm} this
7478      */
7479     clearInvalid : function(){
7480         var items = this.getItems();
7481         
7482         items.each(function(f){
7483            f.clearInvalid();
7484         });
7485         
7486         
7487         
7488         return this;
7489     },
7490
7491     /**
7492      * Resets this form.
7493      * @return {BasicForm} this
7494      */
7495     reset : function(){
7496         var items = this.getItems();
7497         items.each(function(f){
7498             f.reset();
7499         });
7500         
7501         Roo.each(this.childForms || [], function (f) {
7502             f.reset();
7503         });
7504        
7505         
7506         return this;
7507     },
7508     getItems : function()
7509     {
7510         var r=new Roo.util.MixedCollection(false, function(o){
7511             return o.id || (o.id = Roo.id());
7512         });
7513         var iter = function(el) {
7514             if (el.inputEl) {
7515                 r.add(el);
7516             }
7517             if (!el.items) {
7518                 return;
7519             }
7520             Roo.each(el.items,function(e) {
7521                 iter(e);
7522             });
7523             
7524             
7525         };
7526         
7527         iter(this);
7528         return r;
7529         
7530         
7531         
7532         
7533     }
7534     
7535 });
7536
7537  
7538 /*
7539  * Based on:
7540  * Ext JS Library 1.1.1
7541  * Copyright(c) 2006-2007, Ext JS, LLC.
7542  *
7543  * Originally Released Under LGPL - original licence link has changed is not relivant.
7544  *
7545  * Fork - LGPL
7546  * <script type="text/javascript">
7547  */
7548 /**
7549  * @class Roo.form.VTypes
7550  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7551  * @singleton
7552  */
7553 Roo.form.VTypes = function(){
7554     // closure these in so they are only created once.
7555     var alpha = /^[a-zA-Z_]+$/;
7556     var alphanum = /^[a-zA-Z0-9_]+$/;
7557     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7558     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7559
7560     // All these messages and functions are configurable
7561     return {
7562         /**
7563          * The function used to validate email addresses
7564          * @param {String} value The email address
7565          */
7566         'email' : function(v){
7567             return email.test(v);
7568         },
7569         /**
7570          * The error text to display when the email validation function returns false
7571          * @type String
7572          */
7573         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7574         /**
7575          * The keystroke filter mask to be applied on email input
7576          * @type RegExp
7577          */
7578         'emailMask' : /[a-z0-9_\.\-@]/i,
7579
7580         /**
7581          * The function used to validate URLs
7582          * @param {String} value The URL
7583          */
7584         'url' : function(v){
7585             return url.test(v);
7586         },
7587         /**
7588          * The error text to display when the url validation function returns false
7589          * @type String
7590          */
7591         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7592         
7593         /**
7594          * The function used to validate alpha values
7595          * @param {String} value The value
7596          */
7597         'alpha' : function(v){
7598             return alpha.test(v);
7599         },
7600         /**
7601          * The error text to display when the alpha validation function returns false
7602          * @type String
7603          */
7604         'alphaText' : 'This field should only contain letters and _',
7605         /**
7606          * The keystroke filter mask to be applied on alpha input
7607          * @type RegExp
7608          */
7609         'alphaMask' : /[a-z_]/i,
7610
7611         /**
7612          * The function used to validate alphanumeric values
7613          * @param {String} value The value
7614          */
7615         'alphanum' : function(v){
7616             return alphanum.test(v);
7617         },
7618         /**
7619          * The error text to display when the alphanumeric validation function returns false
7620          * @type String
7621          */
7622         'alphanumText' : 'This field should only contain letters, numbers and _',
7623         /**
7624          * The keystroke filter mask to be applied on alphanumeric input
7625          * @type RegExp
7626          */
7627         'alphanumMask' : /[a-z0-9_]/i
7628     };
7629 }();/*
7630  * - LGPL
7631  *
7632  * Input
7633  * 
7634  */
7635
7636 /**
7637  * @class Roo.bootstrap.Input
7638  * @extends Roo.bootstrap.Component
7639  * Bootstrap Input class
7640  * @cfg {Boolean} disabled is it disabled
7641  * @cfg {String} fieldLabel - the label associated
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}  inputType - input / file submit ...
7646  * @cfg {string} placeholder - placeholder to put in text.
7647  * @cfg {string}  before - input group add on before
7648  * @cfg {string} after - input group add on after
7649  * @cfg {string} size - (lg|sm) or leave empty..
7650  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7651  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7652  * @cfg {Number} md colspan out of 12 for computer-sized screens
7653  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7654  * @cfg {string} value default value of the input
7655  * @cfg {Number} labelWidth set the width of label (0-12)
7656  * @cfg {String} labelAlign (top|left)
7657  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7658  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659
7660  * @cfg {String} align (left|center|right) Default left
7661  * @cfg {Boolean} forceFeedback (true|false) Default false
7662  * 
7663  * 
7664  * 
7665  * 
7666  * @constructor
7667  * Create a new Input
7668  * @param {Object} config The config object
7669  */
7670
7671 Roo.bootstrap.Input = function(config){
7672     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7673    
7674         this.addEvents({
7675             /**
7676              * @event focus
7677              * Fires when this field receives input focus.
7678              * @param {Roo.form.Field} this
7679              */
7680             focus : true,
7681             /**
7682              * @event blur
7683              * Fires when this field loses input focus.
7684              * @param {Roo.form.Field} this
7685              */
7686             blur : true,
7687             /**
7688              * @event specialkey
7689              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7690              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7691              * @param {Roo.form.Field} this
7692              * @param {Roo.EventObject} e The event object
7693              */
7694             specialkey : true,
7695             /**
7696              * @event change
7697              * Fires just before the field blurs if the field value has changed.
7698              * @param {Roo.form.Field} this
7699              * @param {Mixed} newValue The new value
7700              * @param {Mixed} oldValue The original value
7701              */
7702             change : true,
7703             /**
7704              * @event invalid
7705              * Fires after the field has been marked as invalid.
7706              * @param {Roo.form.Field} this
7707              * @param {String} msg The validation message
7708              */
7709             invalid : true,
7710             /**
7711              * @event valid
7712              * Fires after the field has been validated with no errors.
7713              * @param {Roo.form.Field} this
7714              */
7715             valid : true,
7716              /**
7717              * @event keyup
7718              * Fires after the key up
7719              * @param {Roo.form.Field} this
7720              * @param {Roo.EventObject}  e The event Object
7721              */
7722             keyup : true
7723         });
7724 };
7725
7726 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7727      /**
7728      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7729       automatic validation (defaults to "keyup").
7730      */
7731     validationEvent : "keyup",
7732      /**
7733      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734      */
7735     validateOnBlur : true,
7736     /**
7737      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738      */
7739     validationDelay : 250,
7740      /**
7741      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742      */
7743     focusClass : "x-form-focus",  // not needed???
7744     
7745        
7746     /**
7747      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748      */
7749     invalidClass : "has-warning",
7750     
7751     /**
7752      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753      */
7754     validClass : "has-success",
7755     
7756     /**
7757      * @cfg {Boolean} hasFeedback (true|false) default true
7758      */
7759     hasFeedback : true,
7760     
7761     /**
7762      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763      */
7764     invalidFeedbackClass : "glyphicon-warning-sign",
7765     
7766     /**
7767      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768      */
7769     validFeedbackClass : "glyphicon-ok",
7770     
7771     /**
7772      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773      */
7774     selectOnFocus : false,
7775     
7776      /**
7777      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7778      */
7779     maskRe : null,
7780        /**
7781      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7782      */
7783     vtype : null,
7784     
7785       /**
7786      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787      */
7788     disableKeyFilter : false,
7789     
7790        /**
7791      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7792      */
7793     disabled : false,
7794      /**
7795      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7796      */
7797     allowBlank : true,
7798     /**
7799      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800      */
7801     blankText : "This field is required",
7802     
7803      /**
7804      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7805      */
7806     minLength : 0,
7807     /**
7808      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809      */
7810     maxLength : Number.MAX_VALUE,
7811     /**
7812      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813      */
7814     minLengthText : "The minimum length for this field is {0}",
7815     /**
7816      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817      */
7818     maxLengthText : "The maximum length for this field is {0}",
7819   
7820     
7821     /**
7822      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7823      * If available, this function will be called only after the basic validators all return true, and will be passed the
7824      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7825      */
7826     validator : null,
7827     /**
7828      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7829      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7830      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7831      */
7832     regex : null,
7833     /**
7834      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7835      */
7836     regexText : "",
7837     
7838     autocomplete: false,
7839     
7840     
7841     fieldLabel : '',
7842     inputType : 'text',
7843     
7844     name : false,
7845     placeholder: false,
7846     before : false,
7847     after : false,
7848     size : false,
7849     hasFocus : false,
7850     preventMark: false,
7851     isFormField : true,
7852     value : '',
7853     labelWidth : 2,
7854     labelAlign : false,
7855     readOnly : false,
7856     align : false,
7857     formatedValue : false,
7858     forceFeedback : false,
7859     
7860     parentLabelAlign : function()
7861     {
7862         var parent = this;
7863         while (parent.parent()) {
7864             parent = parent.parent();
7865             if (typeof(parent.labelAlign) !='undefined') {
7866                 return parent.labelAlign;
7867             }
7868         }
7869         return 'left';
7870         
7871     },
7872     
7873     getAutoCreate : function(){
7874         
7875         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7876         
7877         var id = Roo.id();
7878         
7879         var cfg = {};
7880         
7881         if(this.inputType != 'hidden'){
7882             cfg.cls = 'form-group' //input-group
7883         }
7884         
7885         var input =  {
7886             tag: 'input',
7887             id : id,
7888             type : this.inputType,
7889             value : this.value,
7890             cls : 'form-control',
7891             placeholder : this.placeholder || '',
7892             autocomplete : this.autocomplete || 'new-password'
7893         };
7894         
7895         
7896         if(this.align){
7897             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7898         }
7899         
7900         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7901             input.maxLength = this.maxLength;
7902         }
7903         
7904         if (this.disabled) {
7905             input.disabled=true;
7906         }
7907         
7908         if (this.readOnly) {
7909             input.readonly=true;
7910         }
7911         
7912         if (this.name) {
7913             input.name = this.name;
7914         }
7915         if (this.size) {
7916             input.cls += ' input-' + this.size;
7917         }
7918         var settings=this;
7919         ['xs','sm','md','lg'].map(function(size){
7920             if (settings[size]) {
7921                 cfg.cls += ' col-' + size + '-' + settings[size];
7922             }
7923         });
7924         
7925         var inputblock = input;
7926         
7927         var feedback = {
7928             tag: 'span',
7929             cls: 'glyphicon form-control-feedback'
7930         };
7931             
7932         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7933             
7934             inputblock = {
7935                 cls : 'has-feedback',
7936                 cn :  [
7937                     input,
7938                     feedback
7939                 ] 
7940             };  
7941         }
7942         
7943         if (this.before || this.after) {
7944             
7945             inputblock = {
7946                 cls : 'input-group',
7947                 cn :  [] 
7948             };
7949             
7950             if (this.before && typeof(this.before) == 'string') {
7951                 
7952                 inputblock.cn.push({
7953                     tag :'span',
7954                     cls : 'roo-input-before input-group-addon',
7955                     html : this.before
7956                 });
7957             }
7958             if (this.before && typeof(this.before) == 'object') {
7959                 this.before = Roo.factory(this.before);
7960                 Roo.log(this.before);
7961                 inputblock.cn.push({
7962                     tag :'span',
7963                     cls : 'roo-input-before input-group-' +
7964                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7965                 });
7966             }
7967             
7968             inputblock.cn.push(input);
7969             
7970             if (this.after && typeof(this.after) == 'string') {
7971                 inputblock.cn.push({
7972                     tag :'span',
7973                     cls : 'roo-input-after input-group-addon',
7974                     html : this.after
7975                 });
7976             }
7977             if (this.after && typeof(this.after) == 'object') {
7978                 this.after = Roo.factory(this.after);
7979                 Roo.log(this.after);
7980                 inputblock.cn.push({
7981                     tag :'span',
7982                     cls : 'roo-input-after input-group-' +
7983                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7984                 });
7985             }
7986             
7987             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7988                 inputblock.cls += ' has-feedback';
7989                 inputblock.cn.push(feedback);
7990             }
7991         };
7992         
7993         if (align ==='left' && this.fieldLabel.length) {
7994                 Roo.log("left and has label");
7995                 cfg.cn = [
7996                     
7997                     {
7998                         tag: 'label',
7999                         'for' :  id,
8000                         cls : 'control-label col-sm-' + this.labelWidth,
8001                         html : this.fieldLabel
8002                         
8003                     },
8004                     {
8005                         cls : "col-sm-" + (12 - this.labelWidth), 
8006                         cn: [
8007                             inputblock
8008                         ]
8009                     }
8010                     
8011                 ];
8012         } else if ( this.fieldLabel.length) {
8013                 Roo.log(" label");
8014                  cfg.cn = [
8015                    
8016                     {
8017                         tag: 'label',
8018                         //cls : 'input-group-addon',
8019                         html : this.fieldLabel
8020                         
8021                     },
8022                     
8023                     inputblock
8024                     
8025                 ];
8026
8027         } else {
8028             
8029                 Roo.log(" no label && no align");
8030                 cfg.cn = [
8031                     
8032                         inputblock
8033                     
8034                 ];
8035                 
8036                 
8037         };
8038         Roo.log('input-parentType: ' + this.parentType);
8039         
8040         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8041            cfg.cls += ' navbar-form';
8042            Roo.log(cfg);
8043         }
8044         
8045         return cfg;
8046         
8047     },
8048     /**
8049      * return the real input element.
8050      */
8051     inputEl: function ()
8052     {
8053         return this.el.select('input.form-control',true).first();
8054     },
8055     
8056     tooltipEl : function()
8057     {
8058         return this.inputEl();
8059     },
8060     
8061     setDisabled : function(v)
8062     {
8063         var i  = this.inputEl().dom;
8064         if (!v) {
8065             i.removeAttribute('disabled');
8066             return;
8067             
8068         }
8069         i.setAttribute('disabled','true');
8070     },
8071     initEvents : function()
8072     {
8073           
8074         this.inputEl().on("keydown" , this.fireKey,  this);
8075         this.inputEl().on("focus", this.onFocus,  this);
8076         this.inputEl().on("blur", this.onBlur,  this);
8077         
8078         this.inputEl().relayEvent('keyup', this);
8079  
8080         // reference to original value for reset
8081         this.originalValue = this.getValue();
8082         //Roo.form.TextField.superclass.initEvents.call(this);
8083         if(this.validationEvent == 'keyup'){
8084             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8085             this.inputEl().on('keyup', this.filterValidation, this);
8086         }
8087         else if(this.validationEvent !== false){
8088             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8089         }
8090         
8091         if(this.selectOnFocus){
8092             this.on("focus", this.preFocus, this);
8093             
8094         }
8095         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8096             this.inputEl().on("keypress", this.filterKeys, this);
8097         }
8098        /* if(this.grow){
8099             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8100             this.el.on("click", this.autoSize,  this);
8101         }
8102         */
8103         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8104             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8105         }
8106         
8107         if (typeof(this.before) == 'object') {
8108             this.before.render(this.el.select('.roo-input-before',true).first());
8109         }
8110         if (typeof(this.after) == 'object') {
8111             this.after.render(this.el.select('.roo-input-after',true).first());
8112         }
8113         
8114         
8115     },
8116     filterValidation : function(e){
8117         if(!e.isNavKeyPress()){
8118             this.validationTask.delay(this.validationDelay);
8119         }
8120     },
8121      /**
8122      * Validates the field value
8123      * @return {Boolean} True if the value is valid, else false
8124      */
8125     validate : function(){
8126         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8127         if(this.disabled || this.validateValue(this.getRawValue())){
8128             this.markValid();
8129             return true;
8130         }
8131         
8132         this.markInvalid();
8133         return false;
8134     },
8135     
8136     
8137     /**
8138      * Validates a value according to the field's validation rules and marks the field as invalid
8139      * if the validation fails
8140      * @param {Mixed} value The value to validate
8141      * @return {Boolean} True if the value is valid, else false
8142      */
8143     validateValue : function(value){
8144         if(value.length < 1)  { // if it's blank
8145             if(this.allowBlank){
8146                 return true;
8147             }
8148             return false;
8149         }
8150         
8151         if(value.length < this.minLength){
8152             return false;
8153         }
8154         if(value.length > this.maxLength){
8155             return false;
8156         }
8157         if(this.vtype){
8158             var vt = Roo.form.VTypes;
8159             if(!vt[this.vtype](value, this)){
8160                 return false;
8161             }
8162         }
8163         if(typeof this.validator == "function"){
8164             var msg = this.validator(value);
8165             if(msg !== true){
8166                 return false;
8167             }
8168         }
8169         
8170         if(this.regex && !this.regex.test(value)){
8171             return false;
8172         }
8173         
8174         return true;
8175     },
8176
8177     
8178     
8179      // private
8180     fireKey : function(e){
8181         //Roo.log('field ' + e.getKey());
8182         if(e.isNavKeyPress()){
8183             this.fireEvent("specialkey", this, e);
8184         }
8185     },
8186     focus : function (selectText){
8187         if(this.rendered){
8188             this.inputEl().focus();
8189             if(selectText === true){
8190                 this.inputEl().dom.select();
8191             }
8192         }
8193         return this;
8194     } ,
8195     
8196     onFocus : function(){
8197         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8198            // this.el.addClass(this.focusClass);
8199         }
8200         if(!this.hasFocus){
8201             this.hasFocus = true;
8202             this.startValue = this.getValue();
8203             this.fireEvent("focus", this);
8204         }
8205     },
8206     
8207     beforeBlur : Roo.emptyFn,
8208
8209     
8210     // private
8211     onBlur : function(){
8212         this.beforeBlur();
8213         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214             //this.el.removeClass(this.focusClass);
8215         }
8216         this.hasFocus = false;
8217         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8218             this.validate();
8219         }
8220         var v = this.getValue();
8221         if(String(v) !== String(this.startValue)){
8222             this.fireEvent('change', this, v, this.startValue);
8223         }
8224         this.fireEvent("blur", this);
8225     },
8226     
8227     /**
8228      * Resets the current field value to the originally loaded value and clears any validation messages
8229      */
8230     reset : function(){
8231         this.setValue(this.originalValue);
8232         this.validate();
8233     },
8234      /**
8235      * Returns the name of the field
8236      * @return {Mixed} name The name field
8237      */
8238     getName: function(){
8239         return this.name;
8240     },
8241      /**
8242      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8243      * @return {Mixed} value The field value
8244      */
8245     getValue : function(){
8246         
8247         var v = this.inputEl().getValue();
8248         
8249         return v;
8250     },
8251     /**
8252      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8253      * @return {Mixed} value The field value
8254      */
8255     getRawValue : function(){
8256         var v = this.inputEl().getValue();
8257         
8258         return v;
8259     },
8260     
8261     /**
8262      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8263      * @param {Mixed} value The value to set
8264      */
8265     setRawValue : function(v){
8266         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8267     },
8268     
8269     selectText : function(start, end){
8270         var v = this.getRawValue();
8271         if(v.length > 0){
8272             start = start === undefined ? 0 : start;
8273             end = end === undefined ? v.length : end;
8274             var d = this.inputEl().dom;
8275             if(d.setSelectionRange){
8276                 d.setSelectionRange(start, end);
8277             }else if(d.createTextRange){
8278                 var range = d.createTextRange();
8279                 range.moveStart("character", start);
8280                 range.moveEnd("character", v.length-end);
8281                 range.select();
8282             }
8283         }
8284     },
8285     
8286     /**
8287      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8288      * @param {Mixed} value The value to set
8289      */
8290     setValue : function(v){
8291         this.value = v;
8292         if(this.rendered){
8293             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8294             this.validate();
8295         }
8296     },
8297     
8298     /*
8299     processValue : function(value){
8300         if(this.stripCharsRe){
8301             var newValue = value.replace(this.stripCharsRe, '');
8302             if(newValue !== value){
8303                 this.setRawValue(newValue);
8304                 return newValue;
8305             }
8306         }
8307         return value;
8308     },
8309   */
8310     preFocus : function(){
8311         
8312         if(this.selectOnFocus){
8313             this.inputEl().dom.select();
8314         }
8315     },
8316     filterKeys : function(e){
8317         var k = e.getKey();
8318         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8319             return;
8320         }
8321         var c = e.getCharCode(), cc = String.fromCharCode(c);
8322         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8323             return;
8324         }
8325         if(!this.maskRe.test(cc)){
8326             e.stopEvent();
8327         }
8328     },
8329      /**
8330      * Clear any invalid styles/messages for this field
8331      */
8332     clearInvalid : function(){
8333         
8334         if(!this.el || this.preventMark){ // not rendered
8335             return;
8336         }
8337         this.el.removeClass(this.invalidClass);
8338         
8339         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340             
8341             var feedback = this.el.select('.form-control-feedback', true).first();
8342             
8343             if(feedback){
8344                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8345             }
8346             
8347         }
8348         
8349         this.fireEvent('valid', this);
8350     },
8351     
8352      /**
8353      * Mark this field as valid
8354      */
8355     markValid : function(){
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         this.el.addClass(this.validClass);
8373         
8374         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8375             
8376             var feedback = this.el.select('.form-control-feedback', true).first();
8377             
8378             if(feedback){
8379                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8380                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8381             }
8382             
8383         }
8384         
8385         this.fireEvent('valid', this);
8386     },
8387     
8388      /**
8389      * Mark this field as invalid
8390      * @param {String} msg The validation message
8391      */
8392     markInvalid : function(msg)
8393     {
8394         if(!this.el  || this.preventMark){ // not rendered
8395             return;
8396         }
8397         
8398         this.el.removeClass([this.invalidClass, this.validClass]);
8399         
8400         var feedback = this.el.select('.form-control-feedback', true).first();
8401             
8402         if(feedback){
8403             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8404         }
8405
8406         if(this.disabled || this.allowBlank){
8407             return;
8408         }
8409         
8410         this.el.addClass(this.invalidClass);
8411         
8412         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8413             
8414             var feedback = this.el.select('.form-control-feedback', true).first();
8415             
8416             if(feedback){
8417                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418                 
8419                 if(this.getValue().length || this.forceFeedback){
8420                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8421                 }
8422                 
8423             }
8424             
8425         }
8426         
8427         this.fireEvent('invalid', this, msg);
8428     },
8429     // private
8430     SafariOnKeyDown : function(event)
8431     {
8432         // this is a workaround for a password hang bug on chrome/ webkit.
8433         
8434         var isSelectAll = false;
8435         
8436         if(this.inputEl().dom.selectionEnd > 0){
8437             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8438         }
8439         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8440             event.preventDefault();
8441             this.setValue('');
8442             return;
8443         }
8444         
8445         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8446             
8447             event.preventDefault();
8448             // this is very hacky as keydown always get's upper case.
8449             //
8450             var cc = String.fromCharCode(event.getCharCode());
8451             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8452             
8453         }
8454     },
8455     adjustWidth : function(tag, w){
8456         tag = tag.toLowerCase();
8457         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8458             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8459                 if(tag == 'input'){
8460                     return w + 2;
8461                 }
8462                 if(tag == 'textarea'){
8463                     return w-2;
8464                 }
8465             }else if(Roo.isOpera){
8466                 if(tag == 'input'){
8467                     return w + 2;
8468                 }
8469                 if(tag == 'textarea'){
8470                     return w-2;
8471                 }
8472             }
8473         }
8474         return w;
8475     }
8476     
8477 });
8478
8479  
8480 /*
8481  * - LGPL
8482  *
8483  * Input
8484  * 
8485  */
8486
8487 /**
8488  * @class Roo.bootstrap.TextArea
8489  * @extends Roo.bootstrap.Input
8490  * Bootstrap TextArea class
8491  * @cfg {Number} cols Specifies the visible width of a text area
8492  * @cfg {Number} rows Specifies the visible number of lines in a text area
8493  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8494  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8495  * @cfg {string} html text
8496  * 
8497  * @constructor
8498  * Create a new TextArea
8499  * @param {Object} config The config object
8500  */
8501
8502 Roo.bootstrap.TextArea = function(config){
8503     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8504    
8505 };
8506
8507 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8508      
8509     cols : false,
8510     rows : 5,
8511     readOnly : false,
8512     warp : 'soft',
8513     resize : false,
8514     value: false,
8515     html: false,
8516     
8517     getAutoCreate : function(){
8518         
8519         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8520         
8521         var id = Roo.id();
8522         
8523         var cfg = {};
8524         
8525         var input =  {
8526             tag: 'textarea',
8527             id : id,
8528             warp : this.warp,
8529             rows : this.rows,
8530             value : this.value || '',
8531             html: this.html || '',
8532             cls : 'form-control',
8533             placeholder : this.placeholder || '' 
8534             
8535         };
8536         
8537         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8538             input.maxLength = this.maxLength;
8539         }
8540         
8541         if(this.resize){
8542             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8543         }
8544         
8545         if(this.cols){
8546             input.cols = this.cols;
8547         }
8548         
8549         if (this.readOnly) {
8550             input.readonly = true;
8551         }
8552         
8553         if (this.name) {
8554             input.name = this.name;
8555         }
8556         
8557         if (this.size) {
8558             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8559         }
8560         
8561         var settings=this;
8562         ['xs','sm','md','lg'].map(function(size){
8563             if (settings[size]) {
8564                 cfg.cls += ' col-' + size + '-' + settings[size];
8565             }
8566         });
8567         
8568         var inputblock = input;
8569         
8570         if(this.hasFeedback && !this.allowBlank){
8571             
8572             var feedback = {
8573                 tag: 'span',
8574                 cls: 'glyphicon form-control-feedback'
8575             };
8576
8577             inputblock = {
8578                 cls : 'has-feedback',
8579                 cn :  [
8580                     input,
8581                     feedback
8582                 ] 
8583             };  
8584         }
8585         
8586         
8587         if (this.before || this.after) {
8588             
8589             inputblock = {
8590                 cls : 'input-group',
8591                 cn :  [] 
8592             };
8593             if (this.before) {
8594                 inputblock.cn.push({
8595                     tag :'span',
8596                     cls : 'input-group-addon',
8597                     html : this.before
8598                 });
8599             }
8600             
8601             inputblock.cn.push(input);
8602             
8603             if(this.hasFeedback && !this.allowBlank){
8604                 inputblock.cls += ' has-feedback';
8605                 inputblock.cn.push(feedback);
8606             }
8607             
8608             if (this.after) {
8609                 inputblock.cn.push({
8610                     tag :'span',
8611                     cls : 'input-group-addon',
8612                     html : this.after
8613                 });
8614             }
8615             
8616         }
8617         
8618         if (align ==='left' && this.fieldLabel.length) {
8619                 Roo.log("left and has label");
8620                 cfg.cn = [
8621                     
8622                     {
8623                         tag: 'label',
8624                         'for' :  id,
8625                         cls : 'control-label col-sm-' + this.labelWidth,
8626                         html : this.fieldLabel
8627                         
8628                     },
8629                     {
8630                         cls : "col-sm-" + (12 - this.labelWidth), 
8631                         cn: [
8632                             inputblock
8633                         ]
8634                     }
8635                     
8636                 ];
8637         } else if ( this.fieldLabel.length) {
8638                 Roo.log(" label");
8639                  cfg.cn = [
8640                    
8641                     {
8642                         tag: 'label',
8643                         //cls : 'input-group-addon',
8644                         html : this.fieldLabel
8645                         
8646                     },
8647                     
8648                     inputblock
8649                     
8650                 ];
8651
8652         } else {
8653             
8654                    Roo.log(" no label && no align");
8655                 cfg.cn = [
8656                     
8657                         inputblock
8658                     
8659                 ];
8660                 
8661                 
8662         }
8663         
8664         if (this.disabled) {
8665             input.disabled=true;
8666         }
8667         
8668         return cfg;
8669         
8670     },
8671     /**
8672      * return the real textarea element.
8673      */
8674     inputEl: function ()
8675     {
8676         return this.el.select('textarea.form-control',true).first();
8677     }
8678 });
8679
8680  
8681 /*
8682  * - LGPL
8683  *
8684  * trigger field - base class for combo..
8685  * 
8686  */
8687  
8688 /**
8689  * @class Roo.bootstrap.TriggerField
8690  * @extends Roo.bootstrap.Input
8691  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8692  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8693  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8694  * for which you can provide a custom implementation.  For example:
8695  * <pre><code>
8696 var trigger = new Roo.bootstrap.TriggerField();
8697 trigger.onTriggerClick = myTriggerFn;
8698 trigger.applyTo('my-field');
8699 </code></pre>
8700  *
8701  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8702  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8703  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8704  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8705  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8706
8707  * @constructor
8708  * Create a new TriggerField.
8709  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8710  * to the base TextField)
8711  */
8712 Roo.bootstrap.TriggerField = function(config){
8713     this.mimicing = false;
8714     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8715 };
8716
8717 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8718     /**
8719      * @cfg {String} triggerClass A CSS class to apply to the trigger
8720      */
8721      /**
8722      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8723      */
8724     hideTrigger:false,
8725
8726     /**
8727      * @cfg {Boolean} removable (true|false) special filter default false
8728      */
8729     removable : false,
8730     
8731     /** @cfg {Boolean} grow @hide */
8732     /** @cfg {Number} growMin @hide */
8733     /** @cfg {Number} growMax @hide */
8734
8735     /**
8736      * @hide 
8737      * @method
8738      */
8739     autoSize: Roo.emptyFn,
8740     // private
8741     monitorTab : true,
8742     // private
8743     deferHeight : true,
8744
8745     
8746     actionMode : 'wrap',
8747     
8748     caret : false,
8749     
8750     
8751     getAutoCreate : function(){
8752        
8753         var align = this.labelAlign || this.parentLabelAlign();
8754         
8755         var id = Roo.id();
8756         
8757         var cfg = {
8758             cls: 'form-group' //input-group
8759         };
8760         
8761         
8762         var input =  {
8763             tag: 'input',
8764             id : id,
8765             type : this.inputType,
8766             cls : 'form-control',
8767             autocomplete: 'new-password',
8768             placeholder : this.placeholder || '' 
8769             
8770         };
8771         if (this.name) {
8772             input.name = this.name;
8773         }
8774         if (this.size) {
8775             input.cls += ' input-' + this.size;
8776         }
8777         
8778         if (this.disabled) {
8779             input.disabled=true;
8780         }
8781         
8782         var inputblock = input;
8783         
8784         if(this.hasFeedback && !this.allowBlank){
8785             
8786             var feedback = {
8787                 tag: 'span',
8788                 cls: 'glyphicon form-control-feedback'
8789             };
8790             
8791             if(this.removable && !this.editable && !this.tickable){
8792                 inputblock = {
8793                     cls : 'has-feedback',
8794                     cn :  [
8795                         inputblock,
8796                         {
8797                             tag: 'button',
8798                             html : 'x',
8799                             cls : 'roo-combo-removable-btn close'
8800                         },
8801                         feedback
8802                     ] 
8803                 };
8804             } else {
8805                 inputblock = {
8806                     cls : 'has-feedback',
8807                     cn :  [
8808                         inputblock,
8809                         feedback
8810                     ] 
8811                 };
8812             }
8813
8814         } else {
8815             if(this.removable && !this.editable && !this.tickable){
8816                 inputblock = {
8817                     cls : 'roo-removable',
8818                     cn :  [
8819                         inputblock,
8820                         {
8821                             tag: 'button',
8822                             html : 'x',
8823                             cls : 'roo-combo-removable-btn close'
8824                         }
8825                     ] 
8826                 };
8827             }
8828         }
8829         
8830         if (this.before || this.after) {
8831             
8832             inputblock = {
8833                 cls : 'input-group',
8834                 cn :  [] 
8835             };
8836             if (this.before) {
8837                 inputblock.cn.push({
8838                     tag :'span',
8839                     cls : 'input-group-addon',
8840                     html : this.before
8841                 });
8842             }
8843             
8844             inputblock.cn.push(input);
8845             
8846             if(this.hasFeedback && !this.allowBlank){
8847                 inputblock.cls += ' has-feedback';
8848                 inputblock.cn.push(feedback);
8849             }
8850             
8851             if (this.after) {
8852                 inputblock.cn.push({
8853                     tag :'span',
8854                     cls : 'input-group-addon',
8855                     html : this.after
8856                 });
8857             }
8858             
8859         };
8860         
8861         var box = {
8862             tag: 'div',
8863             cn: [
8864                 {
8865                     tag: 'input',
8866                     type : 'hidden',
8867                     cls: 'form-hidden-field'
8868                 },
8869                 inputblock
8870             ]
8871             
8872         };
8873         
8874         if(this.multiple){
8875             Roo.log('multiple');
8876             
8877             box = {
8878                 tag: 'div',
8879                 cn: [
8880                     {
8881                         tag: 'input',
8882                         type : 'hidden',
8883                         cls: 'form-hidden-field'
8884                     },
8885                     {
8886                         tag: 'ul',
8887                         cls: 'select2-choices',
8888                         cn:[
8889                             {
8890                                 tag: 'li',
8891                                 cls: 'select2-search-field',
8892                                 cn: [
8893
8894                                     inputblock
8895                                 ]
8896                             }
8897                         ]
8898                     }
8899                 ]
8900             }
8901         };
8902         
8903         var combobox = {
8904             cls: 'select2-container input-group',
8905             cn: [
8906                 box
8907 //                {
8908 //                    tag: 'ul',
8909 //                    cls: 'typeahead typeahead-long dropdown-menu',
8910 //                    style: 'display:none'
8911 //                }
8912             ]
8913         };
8914         
8915         if(!this.multiple && this.showToggleBtn){
8916             
8917             var caret = {
8918                         tag: 'span',
8919                         cls: 'caret'
8920              };
8921             if (this.caret != false) {
8922                 caret = {
8923                      tag: 'i',
8924                      cls: 'fa fa-' + this.caret
8925                 };
8926                 
8927             }
8928             
8929             combobox.cn.push({
8930                 tag :'span',
8931                 cls : 'input-group-addon btn dropdown-toggle',
8932                 cn : [
8933                     caret,
8934                     {
8935                         tag: 'span',
8936                         cls: 'combobox-clear',
8937                         cn  : [
8938                             {
8939                                 tag : 'i',
8940                                 cls: 'icon-remove'
8941                             }
8942                         ]
8943                     }
8944                 ]
8945
8946             })
8947         }
8948         
8949         if(this.multiple){
8950             combobox.cls += ' select2-container-multi';
8951         }
8952         
8953         if (align ==='left' && this.fieldLabel.length) {
8954             
8955                 Roo.log("left and has label");
8956                 cfg.cn = [
8957                     
8958                     {
8959                         tag: 'label',
8960                         'for' :  id,
8961                         cls : 'control-label col-sm-' + this.labelWidth,
8962                         html : this.fieldLabel
8963                         
8964                     },
8965                     {
8966                         cls : "col-sm-" + (12 - this.labelWidth), 
8967                         cn: [
8968                             combobox
8969                         ]
8970                     }
8971                     
8972                 ];
8973         } else if ( this.fieldLabel.length) {
8974                 Roo.log(" label");
8975                  cfg.cn = [
8976                    
8977                     {
8978                         tag: 'label',
8979                         //cls : 'input-group-addon',
8980                         html : this.fieldLabel
8981                         
8982                     },
8983                     
8984                     combobox
8985                     
8986                 ];
8987
8988         } else {
8989             
8990                 Roo.log(" no label && no align");
8991                 cfg = combobox
8992                      
8993                 
8994         }
8995          
8996         var settings=this;
8997         ['xs','sm','md','lg'].map(function(size){
8998             if (settings[size]) {
8999                 cfg.cls += ' col-' + size + '-' + settings[size];
9000             }
9001         });
9002         Roo.log(cfg);
9003         return cfg;
9004         
9005     },
9006     
9007     
9008     
9009     // private
9010     onResize : function(w, h){
9011 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9012 //        if(typeof w == 'number'){
9013 //            var x = w - this.trigger.getWidth();
9014 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9015 //            this.trigger.setStyle('left', x+'px');
9016 //        }
9017     },
9018
9019     // private
9020     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9021
9022     // private
9023     getResizeEl : function(){
9024         return this.inputEl();
9025     },
9026
9027     // private
9028     getPositionEl : function(){
9029         return this.inputEl();
9030     },
9031
9032     // private
9033     alignErrorIcon : function(){
9034         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9035     },
9036
9037     // private
9038     initEvents : function(){
9039         
9040         this.createList();
9041         
9042         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9043         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9044         if(!this.multiple && this.showToggleBtn){
9045             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9046             if(this.hideTrigger){
9047                 this.trigger.setDisplayed(false);
9048             }
9049             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9050         }
9051         
9052         if(this.multiple){
9053             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9054         }
9055         
9056         if(this.removable && !this.editable && !this.tickable){
9057             var close = this.closeTriggerEl();
9058             
9059             if(close){
9060                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9061                 close.on('click', this.removeBtnClick, this, close);
9062             }
9063         }
9064         
9065         //this.trigger.addClassOnOver('x-form-trigger-over');
9066         //this.trigger.addClassOnClick('x-form-trigger-click');
9067         
9068         //if(!this.width){
9069         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9070         //}
9071     },
9072     
9073     closeTriggerEl : function()
9074     {
9075         var close = this.el.select('.roo-combo-removable-btn', true).first();
9076         return close ? close : false;
9077     },
9078     
9079     removeBtnClick : function(e, h, el)
9080     {
9081         e.preventDefault();
9082         
9083         if(this.fireEvent("remove", this) !== false){
9084             this.reset();
9085         }
9086     },
9087     
9088     createList : function()
9089     {
9090         this.list = Roo.get(document.body).createChild({
9091             tag: 'ul',
9092             cls: 'typeahead typeahead-long dropdown-menu',
9093             style: 'display:none'
9094         });
9095         
9096         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9097         
9098     },
9099
9100     // private
9101     initTrigger : function(){
9102        
9103     },
9104
9105     // private
9106     onDestroy : function(){
9107         if(this.trigger){
9108             this.trigger.removeAllListeners();
9109           //  this.trigger.remove();
9110         }
9111         //if(this.wrap){
9112         //    this.wrap.remove();
9113         //}
9114         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9115     },
9116
9117     // private
9118     onFocus : function(){
9119         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9120         /*
9121         if(!this.mimicing){
9122             this.wrap.addClass('x-trigger-wrap-focus');
9123             this.mimicing = true;
9124             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9125             if(this.monitorTab){
9126                 this.el.on("keydown", this.checkTab, this);
9127             }
9128         }
9129         */
9130     },
9131
9132     // private
9133     checkTab : function(e){
9134         if(e.getKey() == e.TAB){
9135             this.triggerBlur();
9136         }
9137     },
9138
9139     // private
9140     onBlur : function(){
9141         // do nothing
9142     },
9143
9144     // private
9145     mimicBlur : function(e, t){
9146         /*
9147         if(!this.wrap.contains(t) && this.validateBlur()){
9148             this.triggerBlur();
9149         }
9150         */
9151     },
9152
9153     // private
9154     triggerBlur : function(){
9155         this.mimicing = false;
9156         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9157         if(this.monitorTab){
9158             this.el.un("keydown", this.checkTab, this);
9159         }
9160         //this.wrap.removeClass('x-trigger-wrap-focus');
9161         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9162     },
9163
9164     // private
9165     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9166     validateBlur : function(e, t){
9167         return true;
9168     },
9169
9170     // private
9171     onDisable : function(){
9172         this.inputEl().dom.disabled = true;
9173         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9174         //if(this.wrap){
9175         //    this.wrap.addClass('x-item-disabled');
9176         //}
9177     },
9178
9179     // private
9180     onEnable : function(){
9181         this.inputEl().dom.disabled = false;
9182         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9183         //if(this.wrap){
9184         //    this.el.removeClass('x-item-disabled');
9185         //}
9186     },
9187
9188     // private
9189     onShow : function(){
9190         var ae = this.getActionEl();
9191         
9192         if(ae){
9193             ae.dom.style.display = '';
9194             ae.dom.style.visibility = 'visible';
9195         }
9196     },
9197
9198     // private
9199     
9200     onHide : function(){
9201         var ae = this.getActionEl();
9202         ae.dom.style.display = 'none';
9203     },
9204
9205     /**
9206      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9207      * by an implementing function.
9208      * @method
9209      * @param {EventObject} e
9210      */
9211     onTriggerClick : Roo.emptyFn
9212 });
9213  /*
9214  * Based on:
9215  * Ext JS Library 1.1.1
9216  * Copyright(c) 2006-2007, Ext JS, LLC.
9217  *
9218  * Originally Released Under LGPL - original licence link has changed is not relivant.
9219  *
9220  * Fork - LGPL
9221  * <script type="text/javascript">
9222  */
9223
9224
9225 /**
9226  * @class Roo.data.SortTypes
9227  * @singleton
9228  * Defines the default sorting (casting?) comparison functions used when sorting data.
9229  */
9230 Roo.data.SortTypes = {
9231     /**
9232      * Default sort that does nothing
9233      * @param {Mixed} s The value being converted
9234      * @return {Mixed} The comparison value
9235      */
9236     none : function(s){
9237         return s;
9238     },
9239     
9240     /**
9241      * The regular expression used to strip tags
9242      * @type {RegExp}
9243      * @property
9244      */
9245     stripTagsRE : /<\/?[^>]+>/gi,
9246     
9247     /**
9248      * Strips all HTML tags to sort on text only
9249      * @param {Mixed} s The value being converted
9250      * @return {String} The comparison value
9251      */
9252     asText : function(s){
9253         return String(s).replace(this.stripTagsRE, "");
9254     },
9255     
9256     /**
9257      * Strips all HTML tags to sort on text only - Case insensitive
9258      * @param {Mixed} s The value being converted
9259      * @return {String} The comparison value
9260      */
9261     asUCText : function(s){
9262         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9263     },
9264     
9265     /**
9266      * Case insensitive string
9267      * @param {Mixed} s The value being converted
9268      * @return {String} The comparison value
9269      */
9270     asUCString : function(s) {
9271         return String(s).toUpperCase();
9272     },
9273     
9274     /**
9275      * Date sorting
9276      * @param {Mixed} s The value being converted
9277      * @return {Number} The comparison value
9278      */
9279     asDate : function(s) {
9280         if(!s){
9281             return 0;
9282         }
9283         if(s instanceof Date){
9284             return s.getTime();
9285         }
9286         return Date.parse(String(s));
9287     },
9288     
9289     /**
9290      * Float sorting
9291      * @param {Mixed} s The value being converted
9292      * @return {Float} The comparison value
9293      */
9294     asFloat : function(s) {
9295         var val = parseFloat(String(s).replace(/,/g, ""));
9296         if(isNaN(val)) val = 0;
9297         return val;
9298     },
9299     
9300     /**
9301      * Integer sorting
9302      * @param {Mixed} s The value being converted
9303      * @return {Number} The comparison value
9304      */
9305     asInt : function(s) {
9306         var val = parseInt(String(s).replace(/,/g, ""));
9307         if(isNaN(val)) val = 0;
9308         return val;
9309     }
9310 };/*
9311  * Based on:
9312  * Ext JS Library 1.1.1
9313  * Copyright(c) 2006-2007, Ext JS, LLC.
9314  *
9315  * Originally Released Under LGPL - original licence link has changed is not relivant.
9316  *
9317  * Fork - LGPL
9318  * <script type="text/javascript">
9319  */
9320
9321 /**
9322 * @class Roo.data.Record
9323  * Instances of this class encapsulate both record <em>definition</em> information, and record
9324  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9325  * to access Records cached in an {@link Roo.data.Store} object.<br>
9326  * <p>
9327  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9328  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9329  * objects.<br>
9330  * <p>
9331  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9332  * @constructor
9333  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9334  * {@link #create}. The parameters are the same.
9335  * @param {Array} data An associative Array of data values keyed by the field name.
9336  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9337  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9338  * not specified an integer id is generated.
9339  */
9340 Roo.data.Record = function(data, id){
9341     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9342     this.data = data;
9343 };
9344
9345 /**
9346  * Generate a constructor for a specific record layout.
9347  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9348  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9349  * Each field definition object may contain the following properties: <ul>
9350  * <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,
9351  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9352  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9353  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9354  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9355  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9356  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9357  * this may be omitted.</p></li>
9358  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9359  * <ul><li>auto (Default, implies no conversion)</li>
9360  * <li>string</li>
9361  * <li>int</li>
9362  * <li>float</li>
9363  * <li>boolean</li>
9364  * <li>date</li></ul></p></li>
9365  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9366  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9367  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9368  * by the Reader into an object that will be stored in the Record. It is passed the
9369  * following parameters:<ul>
9370  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9371  * </ul></p></li>
9372  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9373  * </ul>
9374  * <br>usage:<br><pre><code>
9375 var TopicRecord = Roo.data.Record.create(
9376     {name: 'title', mapping: 'topic_title'},
9377     {name: 'author', mapping: 'username'},
9378     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9379     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9380     {name: 'lastPoster', mapping: 'user2'},
9381     {name: 'excerpt', mapping: 'post_text'}
9382 );
9383
9384 var myNewRecord = new TopicRecord({
9385     title: 'Do my job please',
9386     author: 'noobie',
9387     totalPosts: 1,
9388     lastPost: new Date(),
9389     lastPoster: 'Animal',
9390     excerpt: 'No way dude!'
9391 });
9392 myStore.add(myNewRecord);
9393 </code></pre>
9394  * @method create
9395  * @static
9396  */
9397 Roo.data.Record.create = function(o){
9398     var f = function(){
9399         f.superclass.constructor.apply(this, arguments);
9400     };
9401     Roo.extend(f, Roo.data.Record);
9402     var p = f.prototype;
9403     p.fields = new Roo.util.MixedCollection(false, function(field){
9404         return field.name;
9405     });
9406     for(var i = 0, len = o.length; i < len; i++){
9407         p.fields.add(new Roo.data.Field(o[i]));
9408     }
9409     f.getField = function(name){
9410         return p.fields.get(name);  
9411     };
9412     return f;
9413 };
9414
9415 Roo.data.Record.AUTO_ID = 1000;
9416 Roo.data.Record.EDIT = 'edit';
9417 Roo.data.Record.REJECT = 'reject';
9418 Roo.data.Record.COMMIT = 'commit';
9419
9420 Roo.data.Record.prototype = {
9421     /**
9422      * Readonly flag - true if this record has been modified.
9423      * @type Boolean
9424      */
9425     dirty : false,
9426     editing : false,
9427     error: null,
9428     modified: null,
9429
9430     // private
9431     join : function(store){
9432         this.store = store;
9433     },
9434
9435     /**
9436      * Set the named field to the specified value.
9437      * @param {String} name The name of the field to set.
9438      * @param {Object} value The value to set the field to.
9439      */
9440     set : function(name, value){
9441         if(this.data[name] == value){
9442             return;
9443         }
9444         this.dirty = true;
9445         if(!this.modified){
9446             this.modified = {};
9447         }
9448         if(typeof this.modified[name] == 'undefined'){
9449             this.modified[name] = this.data[name];
9450         }
9451         this.data[name] = value;
9452         if(!this.editing && this.store){
9453             this.store.afterEdit(this);
9454         }       
9455     },
9456
9457     /**
9458      * Get the value of the named field.
9459      * @param {String} name The name of the field to get the value of.
9460      * @return {Object} The value of the field.
9461      */
9462     get : function(name){
9463         return this.data[name]; 
9464     },
9465
9466     // private
9467     beginEdit : function(){
9468         this.editing = true;
9469         this.modified = {}; 
9470     },
9471
9472     // private
9473     cancelEdit : function(){
9474         this.editing = false;
9475         delete this.modified;
9476     },
9477
9478     // private
9479     endEdit : function(){
9480         this.editing = false;
9481         if(this.dirty && this.store){
9482             this.store.afterEdit(this);
9483         }
9484     },
9485
9486     /**
9487      * Usually called by the {@link Roo.data.Store} which owns the Record.
9488      * Rejects all changes made to the Record since either creation, or the last commit operation.
9489      * Modified fields are reverted to their original values.
9490      * <p>
9491      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9492      * of reject operations.
9493      */
9494     reject : function(){
9495         var m = this.modified;
9496         for(var n in m){
9497             if(typeof m[n] != "function"){
9498                 this.data[n] = m[n];
9499             }
9500         }
9501         this.dirty = false;
9502         delete this.modified;
9503         this.editing = false;
9504         if(this.store){
9505             this.store.afterReject(this);
9506         }
9507     },
9508
9509     /**
9510      * Usually called by the {@link Roo.data.Store} which owns the Record.
9511      * Commits all changes made to the Record since either creation, or the last commit operation.
9512      * <p>
9513      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9514      * of commit operations.
9515      */
9516     commit : function(){
9517         this.dirty = false;
9518         delete this.modified;
9519         this.editing = false;
9520         if(this.store){
9521             this.store.afterCommit(this);
9522         }
9523     },
9524
9525     // private
9526     hasError : function(){
9527         return this.error != null;
9528     },
9529
9530     // private
9531     clearError : function(){
9532         this.error = null;
9533     },
9534
9535     /**
9536      * Creates a copy of this record.
9537      * @param {String} id (optional) A new record id if you don't want to use this record's id
9538      * @return {Record}
9539      */
9540     copy : function(newId) {
9541         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9542     }
9543 };/*
9544  * Based on:
9545  * Ext JS Library 1.1.1
9546  * Copyright(c) 2006-2007, Ext JS, LLC.
9547  *
9548  * Originally Released Under LGPL - original licence link has changed is not relivant.
9549  *
9550  * Fork - LGPL
9551  * <script type="text/javascript">
9552  */
9553
9554
9555
9556 /**
9557  * @class Roo.data.Store
9558  * @extends Roo.util.Observable
9559  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9560  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9561  * <p>
9562  * 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
9563  * has no knowledge of the format of the data returned by the Proxy.<br>
9564  * <p>
9565  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9566  * instances from the data object. These records are cached and made available through accessor functions.
9567  * @constructor
9568  * Creates a new Store.
9569  * @param {Object} config A config object containing the objects needed for the Store to access data,
9570  * and read the data into Records.
9571  */
9572 Roo.data.Store = function(config){
9573     this.data = new Roo.util.MixedCollection(false);
9574     this.data.getKey = function(o){
9575         return o.id;
9576     };
9577     this.baseParams = {};
9578     // private
9579     this.paramNames = {
9580         "start" : "start",
9581         "limit" : "limit",
9582         "sort" : "sort",
9583         "dir" : "dir",
9584         "multisort" : "_multisort"
9585     };
9586
9587     if(config && config.data){
9588         this.inlineData = config.data;
9589         delete config.data;
9590     }
9591
9592     Roo.apply(this, config);
9593     
9594     if(this.reader){ // reader passed
9595         this.reader = Roo.factory(this.reader, Roo.data);
9596         this.reader.xmodule = this.xmodule || false;
9597         if(!this.recordType){
9598             this.recordType = this.reader.recordType;
9599         }
9600         if(this.reader.onMetaChange){
9601             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9602         }
9603     }
9604
9605     if(this.recordType){
9606         this.fields = this.recordType.prototype.fields;
9607     }
9608     this.modified = [];
9609
9610     this.addEvents({
9611         /**
9612          * @event datachanged
9613          * Fires when the data cache has changed, and a widget which is using this Store
9614          * as a Record cache should refresh its view.
9615          * @param {Store} this
9616          */
9617         datachanged : true,
9618         /**
9619          * @event metachange
9620          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9621          * @param {Store} this
9622          * @param {Object} meta The JSON metadata
9623          */
9624         metachange : true,
9625         /**
9626          * @event add
9627          * Fires when Records have been added to the Store
9628          * @param {Store} this
9629          * @param {Roo.data.Record[]} records The array of Records added
9630          * @param {Number} index The index at which the record(s) were added
9631          */
9632         add : true,
9633         /**
9634          * @event remove
9635          * Fires when a Record has been removed from the Store
9636          * @param {Store} this
9637          * @param {Roo.data.Record} record The Record that was removed
9638          * @param {Number} index The index at which the record was removed
9639          */
9640         remove : true,
9641         /**
9642          * @event update
9643          * Fires when a Record has been updated
9644          * @param {Store} this
9645          * @param {Roo.data.Record} record The Record that was updated
9646          * @param {String} operation The update operation being performed.  Value may be one of:
9647          * <pre><code>
9648  Roo.data.Record.EDIT
9649  Roo.data.Record.REJECT
9650  Roo.data.Record.COMMIT
9651          * </code></pre>
9652          */
9653         update : true,
9654         /**
9655          * @event clear
9656          * Fires when the data cache has been cleared.
9657          * @param {Store} this
9658          */
9659         clear : true,
9660         /**
9661          * @event beforeload
9662          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9663          * the load action will be canceled.
9664          * @param {Store} this
9665          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9666          */
9667         beforeload : true,
9668         /**
9669          * @event beforeloadadd
9670          * Fires after a new set of Records has been loaded.
9671          * @param {Store} this
9672          * @param {Roo.data.Record[]} records The Records that were loaded
9673          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9674          */
9675         beforeloadadd : true,
9676         /**
9677          * @event load
9678          * Fires after a new set of Records has been loaded, before they are added to the store.
9679          * @param {Store} this
9680          * @param {Roo.data.Record[]} records The Records that were loaded
9681          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9682          * @params {Object} return from reader
9683          */
9684         load : true,
9685         /**
9686          * @event loadexception
9687          * Fires if an exception occurs in the Proxy during loading.
9688          * Called with the signature of the Proxy's "loadexception" event.
9689          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9690          * 
9691          * @param {Proxy} 
9692          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9693          * @param {Object} load options 
9694          * @param {Object} jsonData from your request (normally this contains the Exception)
9695          */
9696         loadexception : true
9697     });
9698     
9699     if(this.proxy){
9700         this.proxy = Roo.factory(this.proxy, Roo.data);
9701         this.proxy.xmodule = this.xmodule || false;
9702         this.relayEvents(this.proxy,  ["loadexception"]);
9703     }
9704     this.sortToggle = {};
9705     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9706
9707     Roo.data.Store.superclass.constructor.call(this);
9708
9709     if(this.inlineData){
9710         this.loadData(this.inlineData);
9711         delete this.inlineData;
9712     }
9713 };
9714
9715 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9716      /**
9717     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9718     * without a remote query - used by combo/forms at present.
9719     */
9720     
9721     /**
9722     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9723     */
9724     /**
9725     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9726     */
9727     /**
9728     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9729     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9730     */
9731     /**
9732     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9733     * on any HTTP request
9734     */
9735     /**
9736     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9737     */
9738     /**
9739     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9740     */
9741     multiSort: false,
9742     /**
9743     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9744     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9745     */
9746     remoteSort : false,
9747
9748     /**
9749     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9750      * loaded or when a record is removed. (defaults to false).
9751     */
9752     pruneModifiedRecords : false,
9753
9754     // private
9755     lastOptions : null,
9756
9757     /**
9758      * Add Records to the Store and fires the add event.
9759      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9760      */
9761     add : function(records){
9762         records = [].concat(records);
9763         for(var i = 0, len = records.length; i < len; i++){
9764             records[i].join(this);
9765         }
9766         var index = this.data.length;
9767         this.data.addAll(records);
9768         this.fireEvent("add", this, records, index);
9769     },
9770
9771     /**
9772      * Remove a Record from the Store and fires the remove event.
9773      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9774      */
9775     remove : function(record){
9776         var index = this.data.indexOf(record);
9777         this.data.removeAt(index);
9778         if(this.pruneModifiedRecords){
9779             this.modified.remove(record);
9780         }
9781         this.fireEvent("remove", this, record, index);
9782     },
9783
9784     /**
9785      * Remove all Records from the Store and fires the clear event.
9786      */
9787     removeAll : function(){
9788         this.data.clear();
9789         if(this.pruneModifiedRecords){
9790             this.modified = [];
9791         }
9792         this.fireEvent("clear", this);
9793     },
9794
9795     /**
9796      * Inserts Records to the Store at the given index and fires the add event.
9797      * @param {Number} index The start index at which to insert the passed Records.
9798      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9799      */
9800     insert : function(index, records){
9801         records = [].concat(records);
9802         for(var i = 0, len = records.length; i < len; i++){
9803             this.data.insert(index, records[i]);
9804             records[i].join(this);
9805         }
9806         this.fireEvent("add", this, records, index);
9807     },
9808
9809     /**
9810      * Get the index within the cache of the passed Record.
9811      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9812      * @return {Number} The index of the passed Record. Returns -1 if not found.
9813      */
9814     indexOf : function(record){
9815         return this.data.indexOf(record);
9816     },
9817
9818     /**
9819      * Get the index within the cache of the Record with the passed id.
9820      * @param {String} id The id of the Record to find.
9821      * @return {Number} The index of the Record. Returns -1 if not found.
9822      */
9823     indexOfId : function(id){
9824         return this.data.indexOfKey(id);
9825     },
9826
9827     /**
9828      * Get the Record with the specified id.
9829      * @param {String} id The id of the Record to find.
9830      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9831      */
9832     getById : function(id){
9833         return this.data.key(id);
9834     },
9835
9836     /**
9837      * Get the Record at the specified index.
9838      * @param {Number} index The index of the Record to find.
9839      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9840      */
9841     getAt : function(index){
9842         return this.data.itemAt(index);
9843     },
9844
9845     /**
9846      * Returns a range of Records between specified indices.
9847      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9848      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9849      * @return {Roo.data.Record[]} An array of Records
9850      */
9851     getRange : function(start, end){
9852         return this.data.getRange(start, end);
9853     },
9854
9855     // private
9856     storeOptions : function(o){
9857         o = Roo.apply({}, o);
9858         delete o.callback;
9859         delete o.scope;
9860         this.lastOptions = o;
9861     },
9862
9863     /**
9864      * Loads the Record cache from the configured Proxy using the configured Reader.
9865      * <p>
9866      * If using remote paging, then the first load call must specify the <em>start</em>
9867      * and <em>limit</em> properties in the options.params property to establish the initial
9868      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9869      * <p>
9870      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9871      * and this call will return before the new data has been loaded. Perform any post-processing
9872      * in a callback function, or in a "load" event handler.</strong>
9873      * <p>
9874      * @param {Object} options An object containing properties which control loading options:<ul>
9875      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9876      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9877      * passed the following arguments:<ul>
9878      * <li>r : Roo.data.Record[]</li>
9879      * <li>options: Options object from the load call</li>
9880      * <li>success: Boolean success indicator</li></ul></li>
9881      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9882      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9883      * </ul>
9884      */
9885     load : function(options){
9886         options = options || {};
9887         if(this.fireEvent("beforeload", this, options) !== false){
9888             this.storeOptions(options);
9889             var p = Roo.apply(options.params || {}, this.baseParams);
9890             // if meta was not loaded from remote source.. try requesting it.
9891             if (!this.reader.metaFromRemote) {
9892                 p._requestMeta = 1;
9893             }
9894             if(this.sortInfo && this.remoteSort){
9895                 var pn = this.paramNames;
9896                 p[pn["sort"]] = this.sortInfo.field;
9897                 p[pn["dir"]] = this.sortInfo.direction;
9898             }
9899             if (this.multiSort) {
9900                 var pn = this.paramNames;
9901                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9902             }
9903             
9904             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9905         }
9906     },
9907
9908     /**
9909      * Reloads the Record cache from the configured Proxy using the configured Reader and
9910      * the options from the last load operation performed.
9911      * @param {Object} options (optional) An object containing properties which may override the options
9912      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9913      * the most recently used options are reused).
9914      */
9915     reload : function(options){
9916         this.load(Roo.applyIf(options||{}, this.lastOptions));
9917     },
9918
9919     // private
9920     // Called as a callback by the Reader during a load operation.
9921     loadRecords : function(o, options, success){
9922         if(!o || success === false){
9923             if(success !== false){
9924                 this.fireEvent("load", this, [], options, o);
9925             }
9926             if(options.callback){
9927                 options.callback.call(options.scope || this, [], options, false);
9928             }
9929             return;
9930         }
9931         // if data returned failure - throw an exception.
9932         if (o.success === false) {
9933             // show a message if no listener is registered.
9934             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9935                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9936             }
9937             // loadmask wil be hooked into this..
9938             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9939             return;
9940         }
9941         var r = o.records, t = o.totalRecords || r.length;
9942         
9943         this.fireEvent("beforeloadadd", this, r, options, o);
9944         
9945         if(!options || options.add !== true){
9946             if(this.pruneModifiedRecords){
9947                 this.modified = [];
9948             }
9949             for(var i = 0, len = r.length; i < len; i++){
9950                 r[i].join(this);
9951             }
9952             if(this.snapshot){
9953                 this.data = this.snapshot;
9954                 delete this.snapshot;
9955             }
9956             this.data.clear();
9957             this.data.addAll(r);
9958             this.totalLength = t;
9959             this.applySort();
9960             this.fireEvent("datachanged", this);
9961         }else{
9962             this.totalLength = Math.max(t, this.data.length+r.length);
9963             this.add(r);
9964         }
9965         this.fireEvent("load", this, r, options, o);
9966         if(options.callback){
9967             options.callback.call(options.scope || this, r, options, true);
9968         }
9969     },
9970
9971
9972     /**
9973      * Loads data from a passed data block. A Reader which understands the format of the data
9974      * must have been configured in the constructor.
9975      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9976      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9977      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9978      */
9979     loadData : function(o, append){
9980         var r = this.reader.readRecords(o);
9981         this.loadRecords(r, {add: append}, true);
9982     },
9983
9984     /**
9985      * Gets the number of cached records.
9986      * <p>
9987      * <em>If using paging, this may not be the total size of the dataset. If the data object
9988      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9989      * the data set size</em>
9990      */
9991     getCount : function(){
9992         return this.data.length || 0;
9993     },
9994
9995     /**
9996      * Gets the total number of records in the dataset as returned by the server.
9997      * <p>
9998      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9999      * the dataset size</em>
10000      */
10001     getTotalCount : function(){
10002         return this.totalLength || 0;
10003     },
10004
10005     /**
10006      * Returns the sort state of the Store as an object with two properties:
10007      * <pre><code>
10008  field {String} The name of the field by which the Records are sorted
10009  direction {String} The sort order, "ASC" or "DESC"
10010      * </code></pre>
10011      */
10012     getSortState : function(){
10013         return this.sortInfo;
10014     },
10015
10016     // private
10017     applySort : function(){
10018         if(this.sortInfo && !this.remoteSort){
10019             var s = this.sortInfo, f = s.field;
10020             var st = this.fields.get(f).sortType;
10021             var fn = function(r1, r2){
10022                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10023                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10024             };
10025             this.data.sort(s.direction, fn);
10026             if(this.snapshot && this.snapshot != this.data){
10027                 this.snapshot.sort(s.direction, fn);
10028             }
10029         }
10030     },
10031
10032     /**
10033      * Sets the default sort column and order to be used by the next load operation.
10034      * @param {String} fieldName The name of the field to sort by.
10035      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10036      */
10037     setDefaultSort : function(field, dir){
10038         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10039     },
10040
10041     /**
10042      * Sort the Records.
10043      * If remote sorting is used, the sort is performed on the server, and the cache is
10044      * reloaded. If local sorting is used, the cache is sorted internally.
10045      * @param {String} fieldName The name of the field to sort by.
10046      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10047      */
10048     sort : function(fieldName, dir){
10049         var f = this.fields.get(fieldName);
10050         if(!dir){
10051             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10052             
10053             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10054                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10055             }else{
10056                 dir = f.sortDir;
10057             }
10058         }
10059         this.sortToggle[f.name] = dir;
10060         this.sortInfo = {field: f.name, direction: dir};
10061         if(!this.remoteSort){
10062             this.applySort();
10063             this.fireEvent("datachanged", this);
10064         }else{
10065             this.load(this.lastOptions);
10066         }
10067     },
10068
10069     /**
10070      * Calls the specified function for each of the Records in the cache.
10071      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10072      * Returning <em>false</em> aborts and exits the iteration.
10073      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10074      */
10075     each : function(fn, scope){
10076         this.data.each(fn, scope);
10077     },
10078
10079     /**
10080      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10081      * (e.g., during paging).
10082      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10083      */
10084     getModifiedRecords : function(){
10085         return this.modified;
10086     },
10087
10088     // private
10089     createFilterFn : function(property, value, anyMatch){
10090         if(!value.exec){ // not a regex
10091             value = String(value);
10092             if(value.length == 0){
10093                 return false;
10094             }
10095             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10096         }
10097         return function(r){
10098             return value.test(r.data[property]);
10099         };
10100     },
10101
10102     /**
10103      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10104      * @param {String} property A field on your records
10105      * @param {Number} start The record index to start at (defaults to 0)
10106      * @param {Number} end The last record index to include (defaults to length - 1)
10107      * @return {Number} The sum
10108      */
10109     sum : function(property, start, end){
10110         var rs = this.data.items, v = 0;
10111         start = start || 0;
10112         end = (end || end === 0) ? end : rs.length-1;
10113
10114         for(var i = start; i <= end; i++){
10115             v += (rs[i].data[property] || 0);
10116         }
10117         return v;
10118     },
10119
10120     /**
10121      * Filter the records by a specified property.
10122      * @param {String} field A field on your records
10123      * @param {String/RegExp} value Either a string that the field
10124      * should start with or a RegExp to test against the field
10125      * @param {Boolean} anyMatch True to match any part not just the beginning
10126      */
10127     filter : function(property, value, anyMatch){
10128         var fn = this.createFilterFn(property, value, anyMatch);
10129         return fn ? this.filterBy(fn) : this.clearFilter();
10130     },
10131
10132     /**
10133      * Filter by a function. The specified function will be called with each
10134      * record in this data source. If the function returns true the record is included,
10135      * otherwise it is filtered.
10136      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10137      * @param {Object} scope (optional) The scope of the function (defaults to this)
10138      */
10139     filterBy : function(fn, scope){
10140         this.snapshot = this.snapshot || this.data;
10141         this.data = this.queryBy(fn, scope||this);
10142         this.fireEvent("datachanged", this);
10143     },
10144
10145     /**
10146      * Query the records by a specified property.
10147      * @param {String} field A field on your records
10148      * @param {String/RegExp} value Either a string that the field
10149      * should start with or a RegExp to test against the field
10150      * @param {Boolean} anyMatch True to match any part not just the beginning
10151      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10152      */
10153     query : function(property, value, anyMatch){
10154         var fn = this.createFilterFn(property, value, anyMatch);
10155         return fn ? this.queryBy(fn) : this.data.clone();
10156     },
10157
10158     /**
10159      * Query by a function. The specified function will be called with each
10160      * record in this data source. If the function returns true the record is included
10161      * in the results.
10162      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10163      * @param {Object} scope (optional) The scope of the function (defaults to this)
10164       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10165      **/
10166     queryBy : function(fn, scope){
10167         var data = this.snapshot || this.data;
10168         return data.filterBy(fn, scope||this);
10169     },
10170
10171     /**
10172      * Collects unique values for a particular dataIndex from this store.
10173      * @param {String} dataIndex The property to collect
10174      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10175      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10176      * @return {Array} An array of the unique values
10177      **/
10178     collect : function(dataIndex, allowNull, bypassFilter){
10179         var d = (bypassFilter === true && this.snapshot) ?
10180                 this.snapshot.items : this.data.items;
10181         var v, sv, r = [], l = {};
10182         for(var i = 0, len = d.length; i < len; i++){
10183             v = d[i].data[dataIndex];
10184             sv = String(v);
10185             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10186                 l[sv] = true;
10187                 r[r.length] = v;
10188             }
10189         }
10190         return r;
10191     },
10192
10193     /**
10194      * Revert to a view of the Record cache with no filtering applied.
10195      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10196      */
10197     clearFilter : function(suppressEvent){
10198         if(this.snapshot && this.snapshot != this.data){
10199             this.data = this.snapshot;
10200             delete this.snapshot;
10201             if(suppressEvent !== true){
10202                 this.fireEvent("datachanged", this);
10203             }
10204         }
10205     },
10206
10207     // private
10208     afterEdit : function(record){
10209         if(this.modified.indexOf(record) == -1){
10210             this.modified.push(record);
10211         }
10212         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10213     },
10214     
10215     // private
10216     afterReject : function(record){
10217         this.modified.remove(record);
10218         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10219     },
10220
10221     // private
10222     afterCommit : function(record){
10223         this.modified.remove(record);
10224         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10225     },
10226
10227     /**
10228      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10229      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10230      */
10231     commitChanges : function(){
10232         var m = this.modified.slice(0);
10233         this.modified = [];
10234         for(var i = 0, len = m.length; i < len; i++){
10235             m[i].commit();
10236         }
10237     },
10238
10239     /**
10240      * Cancel outstanding changes on all changed records.
10241      */
10242     rejectChanges : function(){
10243         var m = this.modified.slice(0);
10244         this.modified = [];
10245         for(var i = 0, len = m.length; i < len; i++){
10246             m[i].reject();
10247         }
10248     },
10249
10250     onMetaChange : function(meta, rtype, o){
10251         this.recordType = rtype;
10252         this.fields = rtype.prototype.fields;
10253         delete this.snapshot;
10254         this.sortInfo = meta.sortInfo || this.sortInfo;
10255         this.modified = [];
10256         this.fireEvent('metachange', this, this.reader.meta);
10257     },
10258     
10259     moveIndex : function(data, type)
10260     {
10261         var index = this.indexOf(data);
10262         
10263         var newIndex = index + type;
10264         
10265         this.remove(data);
10266         
10267         this.insert(newIndex, data);
10268         
10269     }
10270 });/*
10271  * Based on:
10272  * Ext JS Library 1.1.1
10273  * Copyright(c) 2006-2007, Ext JS, LLC.
10274  *
10275  * Originally Released Under LGPL - original licence link has changed is not relivant.
10276  *
10277  * Fork - LGPL
10278  * <script type="text/javascript">
10279  */
10280
10281 /**
10282  * @class Roo.data.SimpleStore
10283  * @extends Roo.data.Store
10284  * Small helper class to make creating Stores from Array data easier.
10285  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10286  * @cfg {Array} fields An array of field definition objects, or field name strings.
10287  * @cfg {Array} data The multi-dimensional array of data
10288  * @constructor
10289  * @param {Object} config
10290  */
10291 Roo.data.SimpleStore = function(config){
10292     Roo.data.SimpleStore.superclass.constructor.call(this, {
10293         isLocal : true,
10294         reader: new Roo.data.ArrayReader({
10295                 id: config.id
10296             },
10297             Roo.data.Record.create(config.fields)
10298         ),
10299         proxy : new Roo.data.MemoryProxy(config.data)
10300     });
10301     this.load();
10302 };
10303 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313
10314 /**
10315 /**
10316  * @extends Roo.data.Store
10317  * @class Roo.data.JsonStore
10318  * Small helper class to make creating Stores for JSON data easier. <br/>
10319 <pre><code>
10320 var store = new Roo.data.JsonStore({
10321     url: 'get-images.php',
10322     root: 'images',
10323     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10324 });
10325 </code></pre>
10326  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10327  * JsonReader and HttpProxy (unless inline data is provided).</b>
10328  * @cfg {Array} fields An array of field definition objects, or field name strings.
10329  * @constructor
10330  * @param {Object} config
10331  */
10332 Roo.data.JsonStore = function(c){
10333     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10334         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10335         reader: new Roo.data.JsonReader(c, c.fields)
10336     }));
10337 };
10338 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10339  * Based on:
10340  * Ext JS Library 1.1.1
10341  * Copyright(c) 2006-2007, Ext JS, LLC.
10342  *
10343  * Originally Released Under LGPL - original licence link has changed is not relivant.
10344  *
10345  * Fork - LGPL
10346  * <script type="text/javascript">
10347  */
10348
10349  
10350 Roo.data.Field = function(config){
10351     if(typeof config == "string"){
10352         config = {name: config};
10353     }
10354     Roo.apply(this, config);
10355     
10356     if(!this.type){
10357         this.type = "auto";
10358     }
10359     
10360     var st = Roo.data.SortTypes;
10361     // named sortTypes are supported, here we look them up
10362     if(typeof this.sortType == "string"){
10363         this.sortType = st[this.sortType];
10364     }
10365     
10366     // set default sortType for strings and dates
10367     if(!this.sortType){
10368         switch(this.type){
10369             case "string":
10370                 this.sortType = st.asUCString;
10371                 break;
10372             case "date":
10373                 this.sortType = st.asDate;
10374                 break;
10375             default:
10376                 this.sortType = st.none;
10377         }
10378     }
10379
10380     // define once
10381     var stripRe = /[\$,%]/g;
10382
10383     // prebuilt conversion function for this field, instead of
10384     // switching every time we're reading a value
10385     if(!this.convert){
10386         var cv, dateFormat = this.dateFormat;
10387         switch(this.type){
10388             case "":
10389             case "auto":
10390             case undefined:
10391                 cv = function(v){ return v; };
10392                 break;
10393             case "string":
10394                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10395                 break;
10396             case "int":
10397                 cv = function(v){
10398                     return v !== undefined && v !== null && v !== '' ?
10399                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10400                     };
10401                 break;
10402             case "float":
10403                 cv = function(v){
10404                     return v !== undefined && v !== null && v !== '' ?
10405                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10406                     };
10407                 break;
10408             case "bool":
10409             case "boolean":
10410                 cv = function(v){ return v === true || v === "true" || v == 1; };
10411                 break;
10412             case "date":
10413                 cv = function(v){
10414                     if(!v){
10415                         return '';
10416                     }
10417                     if(v instanceof Date){
10418                         return v;
10419                     }
10420                     if(dateFormat){
10421                         if(dateFormat == "timestamp"){
10422                             return new Date(v*1000);
10423                         }
10424                         return Date.parseDate(v, dateFormat);
10425                     }
10426                     var parsed = Date.parse(v);
10427                     return parsed ? new Date(parsed) : null;
10428                 };
10429              break;
10430             
10431         }
10432         this.convert = cv;
10433     }
10434 };
10435
10436 Roo.data.Field.prototype = {
10437     dateFormat: null,
10438     defaultValue: "",
10439     mapping: null,
10440     sortType : null,
10441     sortDir : "ASC"
10442 };/*
10443  * Based on:
10444  * Ext JS Library 1.1.1
10445  * Copyright(c) 2006-2007, Ext JS, LLC.
10446  *
10447  * Originally Released Under LGPL - original licence link has changed is not relivant.
10448  *
10449  * Fork - LGPL
10450  * <script type="text/javascript">
10451  */
10452  
10453 // Base class for reading structured data from a data source.  This class is intended to be
10454 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10455
10456 /**
10457  * @class Roo.data.DataReader
10458  * Base class for reading structured data from a data source.  This class is intended to be
10459  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10460  */
10461
10462 Roo.data.DataReader = function(meta, recordType){
10463     
10464     this.meta = meta;
10465     
10466     this.recordType = recordType instanceof Array ? 
10467         Roo.data.Record.create(recordType) : recordType;
10468 };
10469
10470 Roo.data.DataReader.prototype = {
10471      /**
10472      * Create an empty record
10473      * @param {Object} data (optional) - overlay some values
10474      * @return {Roo.data.Record} record created.
10475      */
10476     newRow :  function(d) {
10477         var da =  {};
10478         this.recordType.prototype.fields.each(function(c) {
10479             switch( c.type) {
10480                 case 'int' : da[c.name] = 0; break;
10481                 case 'date' : da[c.name] = new Date(); break;
10482                 case 'float' : da[c.name] = 0.0; break;
10483                 case 'boolean' : da[c.name] = false; break;
10484                 default : da[c.name] = ""; break;
10485             }
10486             
10487         });
10488         return new this.recordType(Roo.apply(da, d));
10489     }
10490     
10491 };/*
10492  * Based on:
10493  * Ext JS Library 1.1.1
10494  * Copyright(c) 2006-2007, Ext JS, LLC.
10495  *
10496  * Originally Released Under LGPL - original licence link has changed is not relivant.
10497  *
10498  * Fork - LGPL
10499  * <script type="text/javascript">
10500  */
10501
10502 /**
10503  * @class Roo.data.DataProxy
10504  * @extends Roo.data.Observable
10505  * This class is an abstract base class for implementations which provide retrieval of
10506  * unformatted data objects.<br>
10507  * <p>
10508  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10509  * (of the appropriate type which knows how to parse the data object) to provide a block of
10510  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10511  * <p>
10512  * Custom implementations must implement the load method as described in
10513  * {@link Roo.data.HttpProxy#load}.
10514  */
10515 Roo.data.DataProxy = function(){
10516     this.addEvents({
10517         /**
10518          * @event beforeload
10519          * Fires before a network request is made to retrieve a data object.
10520          * @param {Object} This DataProxy object.
10521          * @param {Object} params The params parameter to the load function.
10522          */
10523         beforeload : true,
10524         /**
10525          * @event load
10526          * Fires before the load method's callback is called.
10527          * @param {Object} This DataProxy object.
10528          * @param {Object} o The data object.
10529          * @param {Object} arg The callback argument object passed to the load function.
10530          */
10531         load : true,
10532         /**
10533          * @event loadexception
10534          * Fires if an Exception occurs during data retrieval.
10535          * @param {Object} This DataProxy object.
10536          * @param {Object} o The data object.
10537          * @param {Object} arg The callback argument object passed to the load function.
10538          * @param {Object} e The Exception.
10539          */
10540         loadexception : true
10541     });
10542     Roo.data.DataProxy.superclass.constructor.call(this);
10543 };
10544
10545 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10546
10547     /**
10548      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10549      */
10550 /*
10551  * Based on:
10552  * Ext JS Library 1.1.1
10553  * Copyright(c) 2006-2007, Ext JS, LLC.
10554  *
10555  * Originally Released Under LGPL - original licence link has changed is not relivant.
10556  *
10557  * Fork - LGPL
10558  * <script type="text/javascript">
10559  */
10560 /**
10561  * @class Roo.data.MemoryProxy
10562  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10563  * to the Reader when its load method is called.
10564  * @constructor
10565  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10566  */
10567 Roo.data.MemoryProxy = function(data){
10568     if (data.data) {
10569         data = data.data;
10570     }
10571     Roo.data.MemoryProxy.superclass.constructor.call(this);
10572     this.data = data;
10573 };
10574
10575 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10576     /**
10577      * Load data from the requested source (in this case an in-memory
10578      * data object passed to the constructor), read the data object into
10579      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10580      * process that block using the passed callback.
10581      * @param {Object} params This parameter is not used by the MemoryProxy class.
10582      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10583      * object into a block of Roo.data.Records.
10584      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10585      * The function must be passed <ul>
10586      * <li>The Record block object</li>
10587      * <li>The "arg" argument from the load function</li>
10588      * <li>A boolean success indicator</li>
10589      * </ul>
10590      * @param {Object} scope The scope in which to call the callback
10591      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10592      */
10593     load : function(params, reader, callback, scope, arg){
10594         params = params || {};
10595         var result;
10596         try {
10597             result = reader.readRecords(this.data);
10598         }catch(e){
10599             this.fireEvent("loadexception", this, arg, null, e);
10600             callback.call(scope, null, arg, false);
10601             return;
10602         }
10603         callback.call(scope, result, arg, true);
10604     },
10605     
10606     // private
10607     update : function(params, records){
10608         
10609     }
10610 });/*
10611  * Based on:
10612  * Ext JS Library 1.1.1
10613  * Copyright(c) 2006-2007, Ext JS, LLC.
10614  *
10615  * Originally Released Under LGPL - original licence link has changed is not relivant.
10616  *
10617  * Fork - LGPL
10618  * <script type="text/javascript">
10619  */
10620 /**
10621  * @class Roo.data.HttpProxy
10622  * @extends Roo.data.DataProxy
10623  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10624  * configured to reference a certain URL.<br><br>
10625  * <p>
10626  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10627  * from which the running page was served.<br><br>
10628  * <p>
10629  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10630  * <p>
10631  * Be aware that to enable the browser to parse an XML document, the server must set
10632  * the Content-Type header in the HTTP response to "text/xml".
10633  * @constructor
10634  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10635  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10636  * will be used to make the request.
10637  */
10638 Roo.data.HttpProxy = function(conn){
10639     Roo.data.HttpProxy.superclass.constructor.call(this);
10640     // is conn a conn config or a real conn?
10641     this.conn = conn;
10642     this.useAjax = !conn || !conn.events;
10643   
10644 };
10645
10646 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10647     // thse are take from connection...
10648     
10649     /**
10650      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10651      */
10652     /**
10653      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10654      * extra parameters to each request made by this object. (defaults to undefined)
10655      */
10656     /**
10657      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10658      *  to each request made by this object. (defaults to undefined)
10659      */
10660     /**
10661      * @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)
10662      */
10663     /**
10664      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10665      */
10666      /**
10667      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10668      * @type Boolean
10669      */
10670   
10671
10672     /**
10673      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10674      * @type Boolean
10675      */
10676     /**
10677      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10678      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10679      * a finer-grained basis than the DataProxy events.
10680      */
10681     getConnection : function(){
10682         return this.useAjax ? Roo.Ajax : this.conn;
10683     },
10684
10685     /**
10686      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10687      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10688      * process that block using the passed callback.
10689      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10690      * for the request to the remote server.
10691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10692      * object into a block of Roo.data.Records.
10693      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10694      * The function must be passed <ul>
10695      * <li>The Record block object</li>
10696      * <li>The "arg" argument from the load function</li>
10697      * <li>A boolean success indicator</li>
10698      * </ul>
10699      * @param {Object} scope The scope in which to call the callback
10700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10701      */
10702     load : function(params, reader, callback, scope, arg){
10703         if(this.fireEvent("beforeload", this, params) !== false){
10704             var  o = {
10705                 params : params || {},
10706                 request: {
10707                     callback : callback,
10708                     scope : scope,
10709                     arg : arg
10710                 },
10711                 reader: reader,
10712                 callback : this.loadResponse,
10713                 scope: this
10714             };
10715             if(this.useAjax){
10716                 Roo.applyIf(o, this.conn);
10717                 if(this.activeRequest){
10718                     Roo.Ajax.abort(this.activeRequest);
10719                 }
10720                 this.activeRequest = Roo.Ajax.request(o);
10721             }else{
10722                 this.conn.request(o);
10723             }
10724         }else{
10725             callback.call(scope||this, null, arg, false);
10726         }
10727     },
10728
10729     // private
10730     loadResponse : function(o, success, response){
10731         delete this.activeRequest;
10732         if(!success){
10733             this.fireEvent("loadexception", this, o, response);
10734             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10735             return;
10736         }
10737         var result;
10738         try {
10739             result = o.reader.read(response);
10740         }catch(e){
10741             this.fireEvent("loadexception", this, o, response, e);
10742             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10743             return;
10744         }
10745         
10746         this.fireEvent("load", this, o, o.request.arg);
10747         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10748     },
10749
10750     // private
10751     update : function(dataSet){
10752
10753     },
10754
10755     // private
10756     updateResponse : function(dataSet){
10757
10758     }
10759 });/*
10760  * Based on:
10761  * Ext JS Library 1.1.1
10762  * Copyright(c) 2006-2007, Ext JS, LLC.
10763  *
10764  * Originally Released Under LGPL - original licence link has changed is not relivant.
10765  *
10766  * Fork - LGPL
10767  * <script type="text/javascript">
10768  */
10769
10770 /**
10771  * @class Roo.data.ScriptTagProxy
10772  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10773  * other than the originating domain of the running page.<br><br>
10774  * <p>
10775  * <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
10776  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10777  * <p>
10778  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10779  * source code that is used as the source inside a &lt;script> tag.<br><br>
10780  * <p>
10781  * In order for the browser to process the returned data, the server must wrap the data object
10782  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10783  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10784  * depending on whether the callback name was passed:
10785  * <p>
10786  * <pre><code>
10787 boolean scriptTag = false;
10788 String cb = request.getParameter("callback");
10789 if (cb != null) {
10790     scriptTag = true;
10791     response.setContentType("text/javascript");
10792 } else {
10793     response.setContentType("application/x-json");
10794 }
10795 Writer out = response.getWriter();
10796 if (scriptTag) {
10797     out.write(cb + "(");
10798 }
10799 out.print(dataBlock.toJsonString());
10800 if (scriptTag) {
10801     out.write(");");
10802 }
10803 </pre></code>
10804  *
10805  * @constructor
10806  * @param {Object} config A configuration object.
10807  */
10808 Roo.data.ScriptTagProxy = function(config){
10809     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10810     Roo.apply(this, config);
10811     this.head = document.getElementsByTagName("head")[0];
10812 };
10813
10814 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10815
10816 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10817     /**
10818      * @cfg {String} url The URL from which to request the data object.
10819      */
10820     /**
10821      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10822      */
10823     timeout : 30000,
10824     /**
10825      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10826      * the server the name of the callback function set up by the load call to process the returned data object.
10827      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10828      * javascript output which calls this named function passing the data object as its only parameter.
10829      */
10830     callbackParam : "callback",
10831     /**
10832      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10833      * name to the request.
10834      */
10835     nocache : true,
10836
10837     /**
10838      * Load data from the configured URL, read the data object into
10839      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10840      * process that block using the passed callback.
10841      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10842      * for the request to the remote server.
10843      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10844      * object into a block of Roo.data.Records.
10845      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10846      * The function must be passed <ul>
10847      * <li>The Record block object</li>
10848      * <li>The "arg" argument from the load function</li>
10849      * <li>A boolean success indicator</li>
10850      * </ul>
10851      * @param {Object} scope The scope in which to call the callback
10852      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10853      */
10854     load : function(params, reader, callback, scope, arg){
10855         if(this.fireEvent("beforeload", this, params) !== false){
10856
10857             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10858
10859             var url = this.url;
10860             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10861             if(this.nocache){
10862                 url += "&_dc=" + (new Date().getTime());
10863             }
10864             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10865             var trans = {
10866                 id : transId,
10867                 cb : "stcCallback"+transId,
10868                 scriptId : "stcScript"+transId,
10869                 params : params,
10870                 arg : arg,
10871                 url : url,
10872                 callback : callback,
10873                 scope : scope,
10874                 reader : reader
10875             };
10876             var conn = this;
10877
10878             window[trans.cb] = function(o){
10879                 conn.handleResponse(o, trans);
10880             };
10881
10882             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10883
10884             if(this.autoAbort !== false){
10885                 this.abort();
10886             }
10887
10888             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10889
10890             var script = document.createElement("script");
10891             script.setAttribute("src", url);
10892             script.setAttribute("type", "text/javascript");
10893             script.setAttribute("id", trans.scriptId);
10894             this.head.appendChild(script);
10895
10896             this.trans = trans;
10897         }else{
10898             callback.call(scope||this, null, arg, false);
10899         }
10900     },
10901
10902     // private
10903     isLoading : function(){
10904         return this.trans ? true : false;
10905     },
10906
10907     /**
10908      * Abort the current server request.
10909      */
10910     abort : function(){
10911         if(this.isLoading()){
10912             this.destroyTrans(this.trans);
10913         }
10914     },
10915
10916     // private
10917     destroyTrans : function(trans, isLoaded){
10918         this.head.removeChild(document.getElementById(trans.scriptId));
10919         clearTimeout(trans.timeoutId);
10920         if(isLoaded){
10921             window[trans.cb] = undefined;
10922             try{
10923                 delete window[trans.cb];
10924             }catch(e){}
10925         }else{
10926             // if hasn't been loaded, wait for load to remove it to prevent script error
10927             window[trans.cb] = function(){
10928                 window[trans.cb] = undefined;
10929                 try{
10930                     delete window[trans.cb];
10931                 }catch(e){}
10932             };
10933         }
10934     },
10935
10936     // private
10937     handleResponse : function(o, trans){
10938         this.trans = false;
10939         this.destroyTrans(trans, true);
10940         var result;
10941         try {
10942             result = trans.reader.readRecords(o);
10943         }catch(e){
10944             this.fireEvent("loadexception", this, o, trans.arg, e);
10945             trans.callback.call(trans.scope||window, null, trans.arg, false);
10946             return;
10947         }
10948         this.fireEvent("load", this, o, trans.arg);
10949         trans.callback.call(trans.scope||window, result, trans.arg, true);
10950     },
10951
10952     // private
10953     handleFailure : function(trans){
10954         this.trans = false;
10955         this.destroyTrans(trans, false);
10956         this.fireEvent("loadexception", this, null, trans.arg);
10957         trans.callback.call(trans.scope||window, null, trans.arg, false);
10958     }
10959 });/*
10960  * Based on:
10961  * Ext JS Library 1.1.1
10962  * Copyright(c) 2006-2007, Ext JS, LLC.
10963  *
10964  * Originally Released Under LGPL - original licence link has changed is not relivant.
10965  *
10966  * Fork - LGPL
10967  * <script type="text/javascript">
10968  */
10969
10970 /**
10971  * @class Roo.data.JsonReader
10972  * @extends Roo.data.DataReader
10973  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10974  * based on mappings in a provided Roo.data.Record constructor.
10975  * 
10976  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10977  * in the reply previously. 
10978  * 
10979  * <p>
10980  * Example code:
10981  * <pre><code>
10982 var RecordDef = Roo.data.Record.create([
10983     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10984     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10985 ]);
10986 var myReader = new Roo.data.JsonReader({
10987     totalProperty: "results",    // The property which contains the total dataset size (optional)
10988     root: "rows",                // The property which contains an Array of row objects
10989     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10990 }, RecordDef);
10991 </code></pre>
10992  * <p>
10993  * This would consume a JSON file like this:
10994  * <pre><code>
10995 { 'results': 2, 'rows': [
10996     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10997     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10998 }
10999 </code></pre>
11000  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11001  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11002  * paged from the remote server.
11003  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11004  * @cfg {String} root name of the property which contains the Array of row objects.
11005  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11006  * @cfg {Array} fields Array of field definition objects
11007  * @constructor
11008  * Create a new JsonReader
11009  * @param {Object} meta Metadata configuration options
11010  * @param {Object} recordType Either an Array of field definition objects,
11011  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11012  */
11013 Roo.data.JsonReader = function(meta, recordType){
11014     
11015     meta = meta || {};
11016     // set some defaults:
11017     Roo.applyIf(meta, {
11018         totalProperty: 'total',
11019         successProperty : 'success',
11020         root : 'data',
11021         id : 'id'
11022     });
11023     
11024     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11025 };
11026 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11027     
11028     /**
11029      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11030      * Used by Store query builder to append _requestMeta to params.
11031      * 
11032      */
11033     metaFromRemote : false,
11034     /**
11035      * This method is only used by a DataProxy which has retrieved data from a remote server.
11036      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11037      * @return {Object} data A data block which is used by an Roo.data.Store object as
11038      * a cache of Roo.data.Records.
11039      */
11040     read : function(response){
11041         var json = response.responseText;
11042        
11043         var o = /* eval:var:o */ eval("("+json+")");
11044         if(!o) {
11045             throw {message: "JsonReader.read: Json object not found"};
11046         }
11047         
11048         if(o.metaData){
11049             
11050             delete this.ef;
11051             this.metaFromRemote = true;
11052             this.meta = o.metaData;
11053             this.recordType = Roo.data.Record.create(o.metaData.fields);
11054             this.onMetaChange(this.meta, this.recordType, o);
11055         }
11056         return this.readRecords(o);
11057     },
11058
11059     // private function a store will implement
11060     onMetaChange : function(meta, recordType, o){
11061
11062     },
11063
11064     /**
11065          * @ignore
11066          */
11067     simpleAccess: function(obj, subsc) {
11068         return obj[subsc];
11069     },
11070
11071         /**
11072          * @ignore
11073          */
11074     getJsonAccessor: function(){
11075         var re = /[\[\.]/;
11076         return function(expr) {
11077             try {
11078                 return(re.test(expr))
11079                     ? new Function("obj", "return obj." + expr)
11080                     : function(obj){
11081                         return obj[expr];
11082                     };
11083             } catch(e){}
11084             return Roo.emptyFn;
11085         };
11086     }(),
11087
11088     /**
11089      * Create a data block containing Roo.data.Records from an XML document.
11090      * @param {Object} o An object which contains an Array of row objects in the property specified
11091      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11092      * which contains the total size of the dataset.
11093      * @return {Object} data A data block which is used by an Roo.data.Store object as
11094      * a cache of Roo.data.Records.
11095      */
11096     readRecords : function(o){
11097         /**
11098          * After any data loads, the raw JSON data is available for further custom processing.
11099          * @type Object
11100          */
11101         this.o = o;
11102         var s = this.meta, Record = this.recordType,
11103             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11104
11105 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11106         if (!this.ef) {
11107             if(s.totalProperty) {
11108                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11109                 }
11110                 if(s.successProperty) {
11111                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11112                 }
11113                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11114                 if (s.id) {
11115                         var g = this.getJsonAccessor(s.id);
11116                         this.getId = function(rec) {
11117                                 var r = g(rec);  
11118                                 return (r === undefined || r === "") ? null : r;
11119                         };
11120                 } else {
11121                         this.getId = function(){return null;};
11122                 }
11123             this.ef = [];
11124             for(var jj = 0; jj < fl; jj++){
11125                 f = fi[jj];
11126                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11127                 this.ef[jj] = this.getJsonAccessor(map);
11128             }
11129         }
11130
11131         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11132         if(s.totalProperty){
11133             var vt = parseInt(this.getTotal(o), 10);
11134             if(!isNaN(vt)){
11135                 totalRecords = vt;
11136             }
11137         }
11138         if(s.successProperty){
11139             var vs = this.getSuccess(o);
11140             if(vs === false || vs === 'false'){
11141                 success = false;
11142             }
11143         }
11144         var records = [];
11145         for(var i = 0; i < c; i++){
11146                 var n = root[i];
11147             var values = {};
11148             var id = this.getId(n);
11149             for(var j = 0; j < fl; j++){
11150                 f = fi[j];
11151             var v = this.ef[j](n);
11152             if (!f.convert) {
11153                 Roo.log('missing convert for ' + f.name);
11154                 Roo.log(f);
11155                 continue;
11156             }
11157             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11158             }
11159             var record = new Record(values, id);
11160             record.json = n;
11161             records[i] = record;
11162         }
11163         return {
11164             raw : o,
11165             success : success,
11166             records : records,
11167             totalRecords : totalRecords
11168         };
11169     }
11170 });/*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181 /**
11182  * @class Roo.data.ArrayReader
11183  * @extends Roo.data.DataReader
11184  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11185  * Each element of that Array represents a row of data fields. The
11186  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11187  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11188  * <p>
11189  * Example code:.
11190  * <pre><code>
11191 var RecordDef = Roo.data.Record.create([
11192     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11193     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11194 ]);
11195 var myReader = new Roo.data.ArrayReader({
11196     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11197 }, RecordDef);
11198 </code></pre>
11199  * <p>
11200  * This would consume an Array like this:
11201  * <pre><code>
11202 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11203   </code></pre>
11204  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11205  * @constructor
11206  * Create a new JsonReader
11207  * @param {Object} meta Metadata configuration options.
11208  * @param {Object} recordType Either an Array of field definition objects
11209  * as specified to {@link Roo.data.Record#create},
11210  * or an {@link Roo.data.Record} object
11211  * created using {@link Roo.data.Record#create}.
11212  */
11213 Roo.data.ArrayReader = function(meta, recordType){
11214     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11215 };
11216
11217 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11218     /**
11219      * Create a data block containing Roo.data.Records from an XML document.
11220      * @param {Object} o An Array of row objects which represents the dataset.
11221      * @return {Object} data A data block which is used by an Roo.data.Store object as
11222      * a cache of Roo.data.Records.
11223      */
11224     readRecords : function(o){
11225         var sid = this.meta ? this.meta.id : null;
11226         var recordType = this.recordType, fields = recordType.prototype.fields;
11227         var records = [];
11228         var root = o;
11229             for(var i = 0; i < root.length; i++){
11230                     var n = root[i];
11231                 var values = {};
11232                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11233                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11234                 var f = fields.items[j];
11235                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11236                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11237                 v = f.convert(v);
11238                 values[f.name] = v;
11239             }
11240                 var record = new recordType(values, id);
11241                 record.json = n;
11242                 records[records.length] = record;
11243             }
11244             return {
11245                 records : records,
11246                 totalRecords : records.length
11247             };
11248     }
11249 });/*
11250  * - LGPL
11251  * * 
11252  */
11253
11254 /**
11255  * @class Roo.bootstrap.ComboBox
11256  * @extends Roo.bootstrap.TriggerField
11257  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11258  * @cfg {Boolean} append (true|false) default false
11259  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11260  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11261  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11262  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11263  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11264  * @cfg {Boolean} animate default true
11265  * @cfg {Boolean} emptyResultText only for touch device
11266  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11267  * @constructor
11268  * Create a new ComboBox.
11269  * @param {Object} config Configuration options
11270  */
11271 Roo.bootstrap.ComboBox = function(config){
11272     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11273     this.addEvents({
11274         /**
11275          * @event expand
11276          * Fires when the dropdown list is expanded
11277              * @param {Roo.bootstrap.ComboBox} combo This combo box
11278              */
11279         'expand' : true,
11280         /**
11281          * @event collapse
11282          * Fires when the dropdown list is collapsed
11283              * @param {Roo.bootstrap.ComboBox} combo This combo box
11284              */
11285         'collapse' : true,
11286         /**
11287          * @event beforeselect
11288          * Fires before a list item is selected. Return false to cancel the selection.
11289              * @param {Roo.bootstrap.ComboBox} combo This combo box
11290              * @param {Roo.data.Record} record The data record returned from the underlying store
11291              * @param {Number} index The index of the selected item in the dropdown list
11292              */
11293         'beforeselect' : true,
11294         /**
11295          * @event select
11296          * Fires when a list item is selected
11297              * @param {Roo.bootstrap.ComboBox} combo This combo box
11298              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11299              * @param {Number} index The index of the selected item in the dropdown list
11300              */
11301         'select' : true,
11302         /**
11303          * @event beforequery
11304          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11305          * The event object passed has these properties:
11306              * @param {Roo.bootstrap.ComboBox} combo This combo box
11307              * @param {String} query The query
11308              * @param {Boolean} forceAll true to force "all" query
11309              * @param {Boolean} cancel true to cancel the query
11310              * @param {Object} e The query event object
11311              */
11312         'beforequery': true,
11313          /**
11314          * @event add
11315          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11316              * @param {Roo.bootstrap.ComboBox} combo This combo box
11317              */
11318         'add' : true,
11319         /**
11320          * @event edit
11321          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11322              * @param {Roo.bootstrap.ComboBox} combo This combo box
11323              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11324              */
11325         'edit' : true,
11326         /**
11327          * @event remove
11328          * Fires when the remove value from the combobox array
11329              * @param {Roo.bootstrap.ComboBox} combo This combo box
11330              */
11331         'remove' : true,
11332         /**
11333          * @event specialfilter
11334          * Fires when specialfilter
11335             * @param {Roo.bootstrap.ComboBox} combo This combo box
11336             */
11337         'specialfilter' : true
11338         
11339     });
11340     
11341     this.item = [];
11342     this.tickItems = [];
11343     
11344     this.selectedIndex = -1;
11345     if(this.mode == 'local'){
11346         if(config.queryDelay === undefined){
11347             this.queryDelay = 10;
11348         }
11349         if(config.minChars === undefined){
11350             this.minChars = 0;
11351         }
11352     }
11353 };
11354
11355 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11356      
11357     /**
11358      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11359      * rendering into an Roo.Editor, defaults to false)
11360      */
11361     /**
11362      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11363      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11364      */
11365     /**
11366      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11367      */
11368     /**
11369      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11370      * the dropdown list (defaults to undefined, with no header element)
11371      */
11372
11373      /**
11374      * @cfg {String/Roo.Template} tpl The template to use to render the output
11375      */
11376      
11377      /**
11378      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11379      */
11380     listWidth: undefined,
11381     /**
11382      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11383      * mode = 'remote' or 'text' if mode = 'local')
11384      */
11385     displayField: undefined,
11386     
11387     /**
11388      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11389      * mode = 'remote' or 'value' if mode = 'local'). 
11390      * Note: use of a valueField requires the user make a selection
11391      * in order for a value to be mapped.
11392      */
11393     valueField: undefined,
11394     
11395     
11396     /**
11397      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11398      * field's data value (defaults to the underlying DOM element's name)
11399      */
11400     hiddenName: undefined,
11401     /**
11402      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11403      */
11404     listClass: '',
11405     /**
11406      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11407      */
11408     selectedClass: 'active',
11409     
11410     /**
11411      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11412      */
11413     shadow:'sides',
11414     /**
11415      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11416      * anchor positions (defaults to 'tl-bl')
11417      */
11418     listAlign: 'tl-bl?',
11419     /**
11420      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11421      */
11422     maxHeight: 300,
11423     /**
11424      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11425      * query specified by the allQuery config option (defaults to 'query')
11426      */
11427     triggerAction: 'query',
11428     /**
11429      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11430      * (defaults to 4, does not apply if editable = false)
11431      */
11432     minChars : 4,
11433     /**
11434      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11435      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11436      */
11437     typeAhead: false,
11438     /**
11439      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11440      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11441      */
11442     queryDelay: 500,
11443     /**
11444      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11445      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11446      */
11447     pageSize: 0,
11448     /**
11449      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11450      * when editable = true (defaults to false)
11451      */
11452     selectOnFocus:false,
11453     /**
11454      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11455      */
11456     queryParam: 'query',
11457     /**
11458      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11459      * when mode = 'remote' (defaults to 'Loading...')
11460      */
11461     loadingText: 'Loading...',
11462     /**
11463      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11464      */
11465     resizable: false,
11466     /**
11467      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11468      */
11469     handleHeight : 8,
11470     /**
11471      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11472      * traditional select (defaults to true)
11473      */
11474     editable: true,
11475     /**
11476      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11477      */
11478     allQuery: '',
11479     /**
11480      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11481      */
11482     mode: 'remote',
11483     /**
11484      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11485      * listWidth has a higher value)
11486      */
11487     minListWidth : 70,
11488     /**
11489      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11490      * allow the user to set arbitrary text into the field (defaults to false)
11491      */
11492     forceSelection:false,
11493     /**
11494      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11495      * if typeAhead = true (defaults to 250)
11496      */
11497     typeAheadDelay : 250,
11498     /**
11499      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11500      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11501      */
11502     valueNotFoundText : undefined,
11503     /**
11504      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11505      */
11506     blockFocus : false,
11507     
11508     /**
11509      * @cfg {Boolean} disableClear Disable showing of clear button.
11510      */
11511     disableClear : false,
11512     /**
11513      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11514      */
11515     alwaysQuery : false,
11516     
11517     /**
11518      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11519      */
11520     multiple : false,
11521     
11522     /**
11523      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11524      */
11525     invalidClass : "has-warning",
11526     
11527     /**
11528      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11529      */
11530     validClass : "has-success",
11531     
11532     /**
11533      * @cfg {Boolean} specialFilter (true|false) special filter default false
11534      */
11535     specialFilter : false,
11536     
11537     /**
11538      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11539      */
11540     mobileTouchView : true,
11541     
11542     //private
11543     addicon : false,
11544     editicon: false,
11545     
11546     page: 0,
11547     hasQuery: false,
11548     append: false,
11549     loadNext: false,
11550     autoFocus : true,
11551     tickable : false,
11552     btnPosition : 'right',
11553     triggerList : true,
11554     showToggleBtn : true,
11555     animate : true,
11556     emptyResultText: 'Empty',
11557     triggerText : 'Select',
11558     
11559     // element that contains real text value.. (when hidden is used..)
11560     
11561     getAutoCreate : function()
11562     {
11563         var cfg = false;
11564         
11565         /*
11566          * Touch Devices
11567          */
11568         
11569         if(Roo.isTouch && this.mobileTouchView){
11570             cfg = this.getAutoCreateTouchView();
11571             return cfg;;
11572         }
11573         
11574         /*
11575          *  Normal ComboBox
11576          */
11577         if(!this.tickable){
11578             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11579             return cfg;
11580         }
11581         
11582         /*
11583          *  ComboBox with tickable selections
11584          */
11585              
11586         var align = this.labelAlign || this.parentLabelAlign();
11587         
11588         cfg = {
11589             cls : 'form-group roo-combobox-tickable' //input-group
11590         };
11591         
11592         var buttons = {
11593             tag : 'div',
11594             cls : 'tickable-buttons',
11595             cn : [
11596                 {
11597                     tag : 'button',
11598                     type : 'button',
11599                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11600                     html : this.triggerText
11601                 },
11602                 {
11603                     tag : 'button',
11604                     type : 'button',
11605                     name : 'ok',
11606                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11607                     html : 'Done'
11608                 },
11609                 {
11610                     tag : 'button',
11611                     type : 'button',
11612                     name : 'cancel',
11613                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11614                     html : 'Cancel'
11615                 }
11616             ]
11617         };
11618         
11619         if(this.editable){
11620             buttons.cn.unshift({
11621                 tag: 'input',
11622                 cls: 'select2-search-field-input'
11623             });
11624         }
11625         
11626         var _this = this;
11627         
11628         Roo.each(buttons.cn, function(c){
11629             if (_this.size) {
11630                 c.cls += ' btn-' + _this.size;
11631             }
11632
11633             if (_this.disabled) {
11634                 c.disabled = true;
11635             }
11636         });
11637         
11638         var box = {
11639             tag: 'div',
11640             cn: [
11641                 {
11642                     tag: 'input',
11643                     type : 'hidden',
11644                     cls: 'form-hidden-field'
11645                 },
11646                 {
11647                     tag: 'ul',
11648                     cls: 'select2-choices',
11649                     cn:[
11650                         {
11651                             tag: 'li',
11652                             cls: 'select2-search-field',
11653                             cn: [
11654
11655                                 buttons
11656                             ]
11657                         }
11658                     ]
11659                 }
11660             ]
11661         }
11662         
11663         var combobox = {
11664             cls: 'select2-container input-group select2-container-multi',
11665             cn: [
11666                 box
11667 //                {
11668 //                    tag: 'ul',
11669 //                    cls: 'typeahead typeahead-long dropdown-menu',
11670 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11671 //                }
11672             ]
11673         };
11674         
11675         if(this.hasFeedback && !this.allowBlank){
11676             
11677             var feedback = {
11678                 tag: 'span',
11679                 cls: 'glyphicon form-control-feedback'
11680             };
11681
11682             combobox.cn.push(feedback);
11683         }
11684         
11685         if (align ==='left' && this.fieldLabel.length) {
11686             
11687                 Roo.log("left and has label");
11688                 cfg.cn = [
11689                     
11690                     {
11691                         tag: 'label',
11692                         'for' :  id,
11693                         cls : 'control-label col-sm-' + this.labelWidth,
11694                         html : this.fieldLabel
11695                         
11696                     },
11697                     {
11698                         cls : "col-sm-" + (12 - this.labelWidth), 
11699                         cn: [
11700                             combobox
11701                         ]
11702                     }
11703                     
11704                 ];
11705         } else if ( this.fieldLabel.length) {
11706                 Roo.log(" label");
11707                  cfg.cn = [
11708                    
11709                     {
11710                         tag: 'label',
11711                         //cls : 'input-group-addon',
11712                         html : this.fieldLabel
11713                         
11714                     },
11715                     
11716                     combobox
11717                     
11718                 ];
11719
11720         } else {
11721             
11722                 Roo.log(" no label && no align");
11723                 cfg = combobox
11724                      
11725                 
11726         }
11727          
11728         var settings=this;
11729         ['xs','sm','md','lg'].map(function(size){
11730             if (settings[size]) {
11731                 cfg.cls += ' col-' + size + '-' + settings[size];
11732             }
11733         });
11734         
11735         return cfg;
11736         
11737     },
11738     
11739     _initEventsCalled : false,
11740     
11741     // private
11742     initEvents: function()
11743     {
11744         
11745         if (this._initEventsCalled) { // as we call render... prevent looping...
11746             return;
11747         }
11748         this._initEventsCalled = true;
11749         
11750         if (!this.store) {
11751             throw "can not find store for combo";
11752         }
11753         
11754         this.store = Roo.factory(this.store, Roo.data);
11755         
11756         // if we are building from html. then this element is so complex, that we can not really
11757         // use the rendered HTML.
11758         // so we have to trash and replace the previous code.
11759         if (Roo.XComponent.build_from_html) {
11760             
11761             // remove this element....
11762             var e = this.el.dom, k=0;
11763             while (e ) { e = e.previousSibling;  ++k;}
11764
11765             this.el.remove();
11766             
11767             this.el=false;
11768             this.rendered = false;
11769             
11770             this.render(this.parent().getChildContainer(true), k);
11771             
11772             
11773             
11774         }
11775         
11776         
11777         /*
11778          * Touch Devices
11779          */
11780         
11781         if(Roo.isTouch && this.mobileTouchView){
11782             this.initTouchView();
11783             return;
11784         }
11785         
11786         if(this.tickable){
11787             this.initTickableEvents();
11788             return;
11789         }
11790         
11791         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11792         
11793         if(this.hiddenName){
11794             
11795             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11796             
11797             this.hiddenField.dom.value =
11798                 this.hiddenValue !== undefined ? this.hiddenValue :
11799                 this.value !== undefined ? this.value : '';
11800
11801             // prevent input submission
11802             this.el.dom.removeAttribute('name');
11803             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11804              
11805              
11806         }
11807         //if(Roo.isGecko){
11808         //    this.el.dom.setAttribute('autocomplete', 'off');
11809         //}
11810         
11811         var cls = 'x-combo-list';
11812         
11813         //this.list = new Roo.Layer({
11814         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11815         //});
11816         
11817         var _this = this;
11818         
11819         (function(){
11820             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11821             _this.list.setWidth(lw);
11822         }).defer(100);
11823         
11824         this.list.on('mouseover', this.onViewOver, this);
11825         this.list.on('mousemove', this.onViewMove, this);
11826         
11827         this.list.on('scroll', this.onViewScroll, this);
11828         
11829         /*
11830         this.list.swallowEvent('mousewheel');
11831         this.assetHeight = 0;
11832
11833         if(this.title){
11834             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11835             this.assetHeight += this.header.getHeight();
11836         }
11837
11838         this.innerList = this.list.createChild({cls:cls+'-inner'});
11839         this.innerList.on('mouseover', this.onViewOver, this);
11840         this.innerList.on('mousemove', this.onViewMove, this);
11841         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11842         
11843         if(this.allowBlank && !this.pageSize && !this.disableClear){
11844             this.footer = this.list.createChild({cls:cls+'-ft'});
11845             this.pageTb = new Roo.Toolbar(this.footer);
11846            
11847         }
11848         if(this.pageSize){
11849             this.footer = this.list.createChild({cls:cls+'-ft'});
11850             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11851                     {pageSize: this.pageSize});
11852             
11853         }
11854         
11855         if (this.pageTb && this.allowBlank && !this.disableClear) {
11856             var _this = this;
11857             this.pageTb.add(new Roo.Toolbar.Fill(), {
11858                 cls: 'x-btn-icon x-btn-clear',
11859                 text: '&#160;',
11860                 handler: function()
11861                 {
11862                     _this.collapse();
11863                     _this.clearValue();
11864                     _this.onSelect(false, -1);
11865                 }
11866             });
11867         }
11868         if (this.footer) {
11869             this.assetHeight += this.footer.getHeight();
11870         }
11871         */
11872             
11873         if(!this.tpl){
11874             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11875         }
11876
11877         this.view = new Roo.View(this.list, this.tpl, {
11878             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11879         });
11880         //this.view.wrapEl.setDisplayed(false);
11881         this.view.on('click', this.onViewClick, this);
11882         
11883         
11884         
11885         this.store.on('beforeload', this.onBeforeLoad, this);
11886         this.store.on('load', this.onLoad, this);
11887         this.store.on('loadexception', this.onLoadException, this);
11888         /*
11889         if(this.resizable){
11890             this.resizer = new Roo.Resizable(this.list,  {
11891                pinned:true, handles:'se'
11892             });
11893             this.resizer.on('resize', function(r, w, h){
11894                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11895                 this.listWidth = w;
11896                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11897                 this.restrictHeight();
11898             }, this);
11899             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11900         }
11901         */
11902         if(!this.editable){
11903             this.editable = true;
11904             this.setEditable(false);
11905         }
11906         
11907         /*
11908         
11909         if (typeof(this.events.add.listeners) != 'undefined') {
11910             
11911             this.addicon = this.wrap.createChild(
11912                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11913        
11914             this.addicon.on('click', function(e) {
11915                 this.fireEvent('add', this);
11916             }, this);
11917         }
11918         if (typeof(this.events.edit.listeners) != 'undefined') {
11919             
11920             this.editicon = this.wrap.createChild(
11921                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11922             if (this.addicon) {
11923                 this.editicon.setStyle('margin-left', '40px');
11924             }
11925             this.editicon.on('click', function(e) {
11926                 
11927                 // we fire even  if inothing is selected..
11928                 this.fireEvent('edit', this, this.lastData );
11929                 
11930             }, this);
11931         }
11932         */
11933         
11934         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11935             "up" : function(e){
11936                 this.inKeyMode = true;
11937                 this.selectPrev();
11938             },
11939
11940             "down" : function(e){
11941                 if(!this.isExpanded()){
11942                     this.onTriggerClick();
11943                 }else{
11944                     this.inKeyMode = true;
11945                     this.selectNext();
11946                 }
11947             },
11948
11949             "enter" : function(e){
11950 //                this.onViewClick();
11951                 //return true;
11952                 this.collapse();
11953                 
11954                 if(this.fireEvent("specialkey", this, e)){
11955                     this.onViewClick(false);
11956                 }
11957                 
11958                 return true;
11959             },
11960
11961             "esc" : function(e){
11962                 this.collapse();
11963             },
11964
11965             "tab" : function(e){
11966                 this.collapse();
11967                 
11968                 if(this.fireEvent("specialkey", this, e)){
11969                     this.onViewClick(false);
11970                 }
11971                 
11972                 return true;
11973             },
11974
11975             scope : this,
11976
11977             doRelay : function(foo, bar, hname){
11978                 if(hname == 'down' || this.scope.isExpanded()){
11979                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11980                 }
11981                 return true;
11982             },
11983
11984             forceKeyDown: true
11985         });
11986         
11987         
11988         this.queryDelay = Math.max(this.queryDelay || 10,
11989                 this.mode == 'local' ? 10 : 250);
11990         
11991         
11992         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11993         
11994         if(this.typeAhead){
11995             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11996         }
11997         if(this.editable !== false){
11998             this.inputEl().on("keyup", this.onKeyUp, this);
11999         }
12000         if(this.forceSelection){
12001             this.inputEl().on('blur', this.doForce, this);
12002         }
12003         
12004         if(this.multiple){
12005             this.choices = this.el.select('ul.select2-choices', true).first();
12006             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12007         }
12008     },
12009     
12010     initTickableEvents: function()
12011     {   
12012         this.createList();
12013         
12014         if(this.hiddenName){
12015             
12016             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12017             
12018             this.hiddenField.dom.value =
12019                 this.hiddenValue !== undefined ? this.hiddenValue :
12020                 this.value !== undefined ? this.value : '';
12021
12022             // prevent input submission
12023             this.el.dom.removeAttribute('name');
12024             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12025              
12026              
12027         }
12028         
12029 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12030         
12031         this.choices = this.el.select('ul.select2-choices', true).first();
12032         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12033         if(this.triggerList){
12034             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12035         }
12036          
12037         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12038         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12039         
12040         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12041         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12042         
12043         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12044         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12045         
12046         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12047         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12048         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12049         
12050         this.okBtn.hide();
12051         this.cancelBtn.hide();
12052         
12053         var _this = this;
12054         
12055         (function(){
12056             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12057             _this.list.setWidth(lw);
12058         }).defer(100);
12059         
12060         this.list.on('mouseover', this.onViewOver, this);
12061         this.list.on('mousemove', this.onViewMove, this);
12062         
12063         this.list.on('scroll', this.onViewScroll, this);
12064         
12065         if(!this.tpl){
12066             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>';
12067         }
12068
12069         this.view = new Roo.View(this.list, this.tpl, {
12070             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12071         });
12072         
12073         //this.view.wrapEl.setDisplayed(false);
12074         this.view.on('click', this.onViewClick, this);
12075         
12076         
12077         
12078         this.store.on('beforeload', this.onBeforeLoad, this);
12079         this.store.on('load', this.onLoad, this);
12080         this.store.on('loadexception', this.onLoadException, this);
12081         
12082         if(this.editable){
12083             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12084                 "up" : function(e){
12085                     this.inKeyMode = true;
12086                     this.selectPrev();
12087                 },
12088
12089                 "down" : function(e){
12090                     this.inKeyMode = true;
12091                     this.selectNext();
12092                 },
12093
12094                 "enter" : function(e){
12095                     if(this.fireEvent("specialkey", this, e)){
12096                         this.onViewClick(false);
12097                     }
12098                     
12099                     return true;
12100                 },
12101
12102                 "esc" : function(e){
12103                     this.onTickableFooterButtonClick(e, false, false);
12104                 },
12105
12106                 "tab" : function(e){
12107                     this.fireEvent("specialkey", this, e);
12108                     
12109                     this.onTickableFooterButtonClick(e, false, false);
12110                     
12111                     return true;
12112                 },
12113
12114                 scope : this,
12115
12116                 doRelay : function(e, fn, key){
12117                     if(this.scope.isExpanded()){
12118                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12119                     }
12120                     return true;
12121                 },
12122
12123                 forceKeyDown: true
12124             });
12125         }
12126         
12127         this.queryDelay = Math.max(this.queryDelay || 10,
12128                 this.mode == 'local' ? 10 : 250);
12129         
12130         
12131         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12132         
12133         if(this.typeAhead){
12134             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12135         }
12136         
12137         if(this.editable !== false){
12138             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12139         }
12140         
12141     },
12142
12143     onDestroy : function(){
12144         if(this.view){
12145             this.view.setStore(null);
12146             this.view.el.removeAllListeners();
12147             this.view.el.remove();
12148             this.view.purgeListeners();
12149         }
12150         if(this.list){
12151             this.list.dom.innerHTML  = '';
12152         }
12153         
12154         if(this.store){
12155             this.store.un('beforeload', this.onBeforeLoad, this);
12156             this.store.un('load', this.onLoad, this);
12157             this.store.un('loadexception', this.onLoadException, this);
12158         }
12159         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12160     },
12161
12162     // private
12163     fireKey : function(e){
12164         if(e.isNavKeyPress() && !this.list.isVisible()){
12165             this.fireEvent("specialkey", this, e);
12166         }
12167     },
12168
12169     // private
12170     onResize: function(w, h){
12171 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12172 //        
12173 //        if(typeof w != 'number'){
12174 //            // we do not handle it!?!?
12175 //            return;
12176 //        }
12177 //        var tw = this.trigger.getWidth();
12178 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12179 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12180 //        var x = w - tw;
12181 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12182 //            
12183 //        //this.trigger.setStyle('left', x+'px');
12184 //        
12185 //        if(this.list && this.listWidth === undefined){
12186 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12187 //            this.list.setWidth(lw);
12188 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12189 //        }
12190         
12191     
12192         
12193     },
12194
12195     /**
12196      * Allow or prevent the user from directly editing the field text.  If false is passed,
12197      * the user will only be able to select from the items defined in the dropdown list.  This method
12198      * is the runtime equivalent of setting the 'editable' config option at config time.
12199      * @param {Boolean} value True to allow the user to directly edit the field text
12200      */
12201     setEditable : function(value){
12202         if(value == this.editable){
12203             return;
12204         }
12205         this.editable = value;
12206         if(!value){
12207             this.inputEl().dom.setAttribute('readOnly', true);
12208             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12209             this.inputEl().addClass('x-combo-noedit');
12210         }else{
12211             this.inputEl().dom.setAttribute('readOnly', false);
12212             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12213             this.inputEl().removeClass('x-combo-noedit');
12214         }
12215     },
12216
12217     // private
12218     
12219     onBeforeLoad : function(combo,opts){
12220         if(!this.hasFocus){
12221             return;
12222         }
12223          if (!opts.add) {
12224             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12225          }
12226         this.restrictHeight();
12227         this.selectedIndex = -1;
12228     },
12229
12230     // private
12231     onLoad : function(){
12232         
12233         this.hasQuery = false;
12234         
12235         if(!this.hasFocus){
12236             return;
12237         }
12238         
12239         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12240             this.loading.hide();
12241         }
12242              
12243         if(this.store.getCount() > 0){
12244             this.expand();
12245             this.restrictHeight();
12246             if(this.lastQuery == this.allQuery){
12247                 if(this.editable && !this.tickable){
12248                     this.inputEl().dom.select();
12249                 }
12250                 
12251                 if(
12252                     !this.selectByValue(this.value, true) &&
12253                     this.autoFocus && 
12254                     (
12255                         !this.store.lastOptions ||
12256                         typeof(this.store.lastOptions.add) == 'undefined' || 
12257                         this.store.lastOptions.add != true
12258                     )
12259                 ){
12260                     this.select(0, true);
12261                 }
12262             }else{
12263                 if(this.autoFocus){
12264                     this.selectNext();
12265                 }
12266                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12267                     this.taTask.delay(this.typeAheadDelay);
12268                 }
12269             }
12270         }else{
12271             this.onEmptyResults();
12272         }
12273         
12274         //this.el.focus();
12275     },
12276     // private
12277     onLoadException : function()
12278     {
12279         this.hasQuery = false;
12280         
12281         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12282             this.loading.hide();
12283         }
12284         
12285         if(this.tickable && this.editable){
12286             return;
12287         }
12288         
12289         this.collapse();
12290         
12291         Roo.log(this.store.reader.jsonData);
12292         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12293             // fixme
12294             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12295         }
12296         
12297         
12298     },
12299     // private
12300     onTypeAhead : function(){
12301         if(this.store.getCount() > 0){
12302             var r = this.store.getAt(0);
12303             var newValue = r.data[this.displayField];
12304             var len = newValue.length;
12305             var selStart = this.getRawValue().length;
12306             
12307             if(selStart != len){
12308                 this.setRawValue(newValue);
12309                 this.selectText(selStart, newValue.length);
12310             }
12311         }
12312     },
12313
12314     // private
12315     onSelect : function(record, index){
12316         
12317         if(this.fireEvent('beforeselect', this, record, index) !== false){
12318         
12319             this.setFromData(index > -1 ? record.data : false);
12320             
12321             this.collapse();
12322             this.fireEvent('select', this, record, index);
12323         }
12324     },
12325
12326     /**
12327      * Returns the currently selected field value or empty string if no value is set.
12328      * @return {String} value The selected value
12329      */
12330     getValue : function(){
12331         
12332         if(this.multiple){
12333             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12334         }
12335         
12336         if(this.valueField){
12337             return typeof this.value != 'undefined' ? this.value : '';
12338         }else{
12339             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12340         }
12341     },
12342
12343     /**
12344      * Clears any text/value currently set in the field
12345      */
12346     clearValue : function(){
12347         if(this.hiddenField){
12348             this.hiddenField.dom.value = '';
12349         }
12350         this.value = '';
12351         this.setRawValue('');
12352         this.lastSelectionText = '';
12353         this.lastData = false;
12354         
12355         var close = this.closeTriggerEl();
12356         
12357         if(close){
12358             close.hide();
12359         }
12360         
12361     },
12362
12363     /**
12364      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12365      * will be displayed in the field.  If the value does not match the data value of an existing item,
12366      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12367      * Otherwise the field will be blank (although the value will still be set).
12368      * @param {String} value The value to match
12369      */
12370     setValue : function(v){
12371         if(this.multiple){
12372             this.syncValue();
12373             return;
12374         }
12375         
12376         var text = v;
12377         if(this.valueField){
12378             var r = this.findRecord(this.valueField, v);
12379             if(r){
12380                 text = r.data[this.displayField];
12381             }else if(this.valueNotFoundText !== undefined){
12382                 text = this.valueNotFoundText;
12383             }
12384         }
12385         this.lastSelectionText = text;
12386         if(this.hiddenField){
12387             this.hiddenField.dom.value = v;
12388         }
12389         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12390         this.value = v;
12391         
12392         var close = this.closeTriggerEl();
12393         
12394         if(close){
12395             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12396         }
12397     },
12398     /**
12399      * @property {Object} the last set data for the element
12400      */
12401     
12402     lastData : false,
12403     /**
12404      * Sets the value of the field based on a object which is related to the record format for the store.
12405      * @param {Object} value the value to set as. or false on reset?
12406      */
12407     setFromData : function(o){
12408         
12409         if(this.multiple){
12410             this.addItem(o);
12411             return;
12412         }
12413             
12414         var dv = ''; // display value
12415         var vv = ''; // value value..
12416         this.lastData = o;
12417         if (this.displayField) {
12418             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12419         } else {
12420             // this is an error condition!!!
12421             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12422         }
12423         
12424         if(this.valueField){
12425             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12426         }
12427         
12428         var close = this.closeTriggerEl();
12429         
12430         if(close){
12431             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12432         }
12433         
12434         if(this.hiddenField){
12435             this.hiddenField.dom.value = vv;
12436             
12437             this.lastSelectionText = dv;
12438             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12439             this.value = vv;
12440             return;
12441         }
12442         // no hidden field.. - we store the value in 'value', but still display
12443         // display field!!!!
12444         this.lastSelectionText = dv;
12445         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12446         this.value = vv;
12447         
12448         
12449         
12450     },
12451     // private
12452     reset : function(){
12453         // overridden so that last data is reset..
12454         
12455         if(this.multiple){
12456             this.clearItem();
12457             return;
12458         }
12459         
12460         this.setValue(this.originalValue);
12461         this.clearInvalid();
12462         this.lastData = false;
12463         if (this.view) {
12464             this.view.clearSelections();
12465         }
12466     },
12467     // private
12468     findRecord : function(prop, value){
12469         var record;
12470         if(this.store.getCount() > 0){
12471             this.store.each(function(r){
12472                 if(r.data[prop] == value){
12473                     record = r;
12474                     return false;
12475                 }
12476                 return true;
12477             });
12478         }
12479         return record;
12480     },
12481     
12482     getName: function()
12483     {
12484         // returns hidden if it's set..
12485         if (!this.rendered) {return ''};
12486         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12487         
12488     },
12489     // private
12490     onViewMove : function(e, t){
12491         this.inKeyMode = false;
12492     },
12493
12494     // private
12495     onViewOver : function(e, t){
12496         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12497             return;
12498         }
12499         var item = this.view.findItemFromChild(t);
12500         
12501         if(item){
12502             var index = this.view.indexOf(item);
12503             this.select(index, false);
12504         }
12505     },
12506
12507     // private
12508     onViewClick : function(view, doFocus, el, e)
12509     {
12510         var index = this.view.getSelectedIndexes()[0];
12511         
12512         var r = this.store.getAt(index);
12513         
12514         if(this.tickable){
12515             
12516             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12517                 return;
12518             }
12519             
12520             var rm = false;
12521             var _this = this;
12522             
12523             Roo.each(this.tickItems, function(v,k){
12524                 
12525                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12526                     _this.tickItems.splice(k, 1);
12527                     
12528                     if(typeof(e) == 'undefined' && view == false){
12529                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12530                     }
12531                     
12532                     rm = true;
12533                     return;
12534                 }
12535             });
12536             
12537             if(rm){
12538                 return;
12539             }
12540             
12541             this.tickItems.push(r.data);
12542             
12543             if(typeof(e) == 'undefined' && view == false){
12544                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12545             }
12546                     
12547             return;
12548         }
12549         
12550         if(r){
12551             this.onSelect(r, index);
12552         }
12553         if(doFocus !== false && !this.blockFocus){
12554             this.inputEl().focus();
12555         }
12556     },
12557
12558     // private
12559     restrictHeight : function(){
12560         //this.innerList.dom.style.height = '';
12561         //var inner = this.innerList.dom;
12562         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12563         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12564         //this.list.beginUpdate();
12565         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12566         this.list.alignTo(this.inputEl(), this.listAlign);
12567         this.list.alignTo(this.inputEl(), this.listAlign);
12568         //this.list.endUpdate();
12569     },
12570
12571     // private
12572     onEmptyResults : function(){
12573         
12574         if(this.tickable && this.editable){
12575             this.restrictHeight();
12576             return;
12577         }
12578         
12579         this.collapse();
12580     },
12581
12582     /**
12583      * Returns true if the dropdown list is expanded, else false.
12584      */
12585     isExpanded : function(){
12586         return this.list.isVisible();
12587     },
12588
12589     /**
12590      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12591      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12592      * @param {String} value The data value of the item to select
12593      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12594      * selected item if it is not currently in view (defaults to true)
12595      * @return {Boolean} True if the value matched an item in the list, else false
12596      */
12597     selectByValue : function(v, scrollIntoView){
12598         if(v !== undefined && v !== null){
12599             var r = this.findRecord(this.valueField || this.displayField, v);
12600             if(r){
12601                 this.select(this.store.indexOf(r), scrollIntoView);
12602                 return true;
12603             }
12604         }
12605         return false;
12606     },
12607
12608     /**
12609      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12610      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12611      * @param {Number} index The zero-based index of the list item to select
12612      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12613      * selected item if it is not currently in view (defaults to true)
12614      */
12615     select : function(index, scrollIntoView){
12616         this.selectedIndex = index;
12617         this.view.select(index);
12618         if(scrollIntoView !== false){
12619             var el = this.view.getNode(index);
12620             /*
12621              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12622              */
12623             if(el){
12624                 this.list.scrollChildIntoView(el, false);
12625             }
12626         }
12627     },
12628
12629     // private
12630     selectNext : function(){
12631         var ct = this.store.getCount();
12632         if(ct > 0){
12633             if(this.selectedIndex == -1){
12634                 this.select(0);
12635             }else if(this.selectedIndex < ct-1){
12636                 this.select(this.selectedIndex+1);
12637             }
12638         }
12639     },
12640
12641     // private
12642     selectPrev : function(){
12643         var ct = this.store.getCount();
12644         if(ct > 0){
12645             if(this.selectedIndex == -1){
12646                 this.select(0);
12647             }else if(this.selectedIndex != 0){
12648                 this.select(this.selectedIndex-1);
12649             }
12650         }
12651     },
12652
12653     // private
12654     onKeyUp : function(e){
12655         if(this.editable !== false && !e.isSpecialKey()){
12656             this.lastKey = e.getKey();
12657             this.dqTask.delay(this.queryDelay);
12658         }
12659     },
12660
12661     // private
12662     validateBlur : function(){
12663         return !this.list || !this.list.isVisible();   
12664     },
12665
12666     // private
12667     initQuery : function(){
12668         
12669         var v = this.getRawValue();
12670         
12671         if(this.tickable && this.editable){
12672             v = this.tickableInputEl().getValue();
12673         }
12674         
12675         this.doQuery(v);
12676     },
12677
12678     // private
12679     doForce : function(){
12680         if(this.inputEl().dom.value.length > 0){
12681             this.inputEl().dom.value =
12682                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12683              
12684         }
12685     },
12686
12687     /**
12688      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12689      * query allowing the query action to be canceled if needed.
12690      * @param {String} query The SQL query to execute
12691      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12692      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12693      * saved in the current store (defaults to false)
12694      */
12695     doQuery : function(q, forceAll){
12696         
12697         if(q === undefined || q === null){
12698             q = '';
12699         }
12700         var qe = {
12701             query: q,
12702             forceAll: forceAll,
12703             combo: this,
12704             cancel:false
12705         };
12706         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12707             return false;
12708         }
12709         q = qe.query;
12710         
12711         forceAll = qe.forceAll;
12712         if(forceAll === true || (q.length >= this.minChars)){
12713             
12714             this.hasQuery = true;
12715             
12716             if(this.lastQuery != q || this.alwaysQuery){
12717                 this.lastQuery = q;
12718                 if(this.mode == 'local'){
12719                     this.selectedIndex = -1;
12720                     if(forceAll){
12721                         this.store.clearFilter();
12722                     }else{
12723                         
12724                         if(this.specialFilter){
12725                             this.fireEvent('specialfilter', this);
12726                             this.onLoad();
12727                             return;
12728                         }
12729                         
12730                         this.store.filter(this.displayField, q);
12731                     }
12732                     
12733                     this.store.fireEvent("datachanged", this.store);
12734                     
12735                     this.onLoad();
12736                     
12737                     
12738                 }else{
12739                     
12740                     this.store.baseParams[this.queryParam] = q;
12741                     
12742                     var options = {params : this.getParams(q)};
12743                     
12744                     if(this.loadNext){
12745                         options.add = true;
12746                         options.params.start = this.page * this.pageSize;
12747                     }
12748                     
12749                     this.store.load(options);
12750                     
12751                     /*
12752                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12753                      *  we should expand the list on onLoad
12754                      *  so command out it
12755                      */
12756 //                    this.expand();
12757                 }
12758             }else{
12759                 this.selectedIndex = -1;
12760                 this.onLoad();   
12761             }
12762         }
12763         
12764         this.loadNext = false;
12765     },
12766     
12767     // private
12768     getParams : function(q){
12769         var p = {};
12770         //p[this.queryParam] = q;
12771         
12772         if(this.pageSize){
12773             p.start = 0;
12774             p.limit = this.pageSize;
12775         }
12776         return p;
12777     },
12778
12779     /**
12780      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12781      */
12782     collapse : function(){
12783         if(!this.isExpanded()){
12784             return;
12785         }
12786         
12787         this.list.hide();
12788         
12789         if(this.tickable){
12790             this.hasFocus = false;
12791             this.okBtn.hide();
12792             this.cancelBtn.hide();
12793             this.trigger.show();
12794             
12795             if(this.editable){
12796                 this.tickableInputEl().dom.value = '';
12797                 this.tickableInputEl().blur();
12798             }
12799             
12800         }
12801         
12802         Roo.get(document).un('mousedown', this.collapseIf, this);
12803         Roo.get(document).un('mousewheel', this.collapseIf, this);
12804         if (!this.editable) {
12805             Roo.get(document).un('keydown', this.listKeyPress, this);
12806         }
12807         this.fireEvent('collapse', this);
12808     },
12809
12810     // private
12811     collapseIf : function(e){
12812         var in_combo  = e.within(this.el);
12813         var in_list =  e.within(this.list);
12814         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12815         
12816         if (in_combo || in_list || is_list) {
12817             //e.stopPropagation();
12818             return;
12819         }
12820         
12821         if(this.tickable){
12822             this.onTickableFooterButtonClick(e, false, false);
12823         }
12824
12825         this.collapse();
12826         
12827     },
12828
12829     /**
12830      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12831      */
12832     expand : function(){
12833        
12834         if(this.isExpanded() || !this.hasFocus){
12835             return;
12836         }
12837         
12838         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12839         this.list.setWidth(lw);
12840         
12841         
12842          Roo.log('expand');
12843         
12844         this.list.show();
12845         
12846         this.restrictHeight();
12847         
12848         if(this.tickable){
12849             
12850             this.tickItems = Roo.apply([], this.item);
12851             
12852             this.okBtn.show();
12853             this.cancelBtn.show();
12854             this.trigger.hide();
12855             
12856             if(this.editable){
12857                 this.tickableInputEl().focus();
12858             }
12859             
12860         }
12861         
12862         Roo.get(document).on('mousedown', this.collapseIf, this);
12863         Roo.get(document).on('mousewheel', this.collapseIf, this);
12864         if (!this.editable) {
12865             Roo.get(document).on('keydown', this.listKeyPress, this);
12866         }
12867         
12868         this.fireEvent('expand', this);
12869     },
12870
12871     // private
12872     // Implements the default empty TriggerField.onTriggerClick function
12873     onTriggerClick : function(e)
12874     {
12875         Roo.log('trigger click');
12876         
12877         if(this.disabled || !this.triggerList){
12878             return;
12879         }
12880         
12881         this.page = 0;
12882         this.loadNext = false;
12883         
12884         if(this.isExpanded()){
12885             this.collapse();
12886             if (!this.blockFocus) {
12887                 this.inputEl().focus();
12888             }
12889             
12890         }else {
12891             this.hasFocus = true;
12892             if(this.triggerAction == 'all') {
12893                 this.doQuery(this.allQuery, true);
12894             } else {
12895                 this.doQuery(this.getRawValue());
12896             }
12897             if (!this.blockFocus) {
12898                 this.inputEl().focus();
12899             }
12900         }
12901     },
12902     
12903     onTickableTriggerClick : function(e)
12904     {
12905         if(this.disabled){
12906             return;
12907         }
12908         
12909         this.page = 0;
12910         this.loadNext = false;
12911         this.hasFocus = true;
12912         
12913         if(this.triggerAction == 'all') {
12914             this.doQuery(this.allQuery, true);
12915         } else {
12916             this.doQuery(this.getRawValue());
12917         }
12918     },
12919     
12920     onSearchFieldClick : function(e)
12921     {
12922         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12923             this.onTickableFooterButtonClick(e, false, false);
12924             return;
12925         }
12926         
12927         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12928             return;
12929         }
12930         
12931         this.page = 0;
12932         this.loadNext = false;
12933         this.hasFocus = true;
12934         
12935         if(this.triggerAction == 'all') {
12936             this.doQuery(this.allQuery, true);
12937         } else {
12938             this.doQuery(this.getRawValue());
12939         }
12940     },
12941     
12942     listKeyPress : function(e)
12943     {
12944         //Roo.log('listkeypress');
12945         // scroll to first matching element based on key pres..
12946         if (e.isSpecialKey()) {
12947             return false;
12948         }
12949         var k = String.fromCharCode(e.getKey()).toUpperCase();
12950         //Roo.log(k);
12951         var match  = false;
12952         var csel = this.view.getSelectedNodes();
12953         var cselitem = false;
12954         if (csel.length) {
12955             var ix = this.view.indexOf(csel[0]);
12956             cselitem  = this.store.getAt(ix);
12957             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12958                 cselitem = false;
12959             }
12960             
12961         }
12962         
12963         this.store.each(function(v) { 
12964             if (cselitem) {
12965                 // start at existing selection.
12966                 if (cselitem.id == v.id) {
12967                     cselitem = false;
12968                 }
12969                 return true;
12970             }
12971                 
12972             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12973                 match = this.store.indexOf(v);
12974                 return false;
12975             }
12976             return true;
12977         }, this);
12978         
12979         if (match === false) {
12980             return true; // no more action?
12981         }
12982         // scroll to?
12983         this.view.select(match);
12984         var sn = Roo.get(this.view.getSelectedNodes()[0])
12985         sn.scrollIntoView(sn.dom.parentNode, false);
12986     },
12987     
12988     onViewScroll : function(e, t){
12989         
12990         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){
12991             return;
12992         }
12993         
12994         this.hasQuery = true;
12995         
12996         this.loading = this.list.select('.loading', true).first();
12997         
12998         if(this.loading === null){
12999             this.list.createChild({
13000                 tag: 'div',
13001                 cls: 'loading select2-more-results select2-active',
13002                 html: 'Loading more results...'
13003             })
13004             
13005             this.loading = this.list.select('.loading', true).first();
13006             
13007             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13008             
13009             this.loading.hide();
13010         }
13011         
13012         this.loading.show();
13013         
13014         var _combo = this;
13015         
13016         this.page++;
13017         this.loadNext = true;
13018         
13019         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13020         
13021         return;
13022     },
13023     
13024     addItem : function(o)
13025     {   
13026         var dv = ''; // display value
13027         
13028         if (this.displayField) {
13029             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13030         } else {
13031             // this is an error condition!!!
13032             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13033         }
13034         
13035         if(!dv.length){
13036             return;
13037         }
13038         
13039         var choice = this.choices.createChild({
13040             tag: 'li',
13041             cls: 'select2-search-choice',
13042             cn: [
13043                 {
13044                     tag: 'div',
13045                     html: dv
13046                 },
13047                 {
13048                     tag: 'a',
13049                     href: '#',
13050                     cls: 'select2-search-choice-close',
13051                     tabindex: '-1'
13052                 }
13053             ]
13054             
13055         }, this.searchField);
13056         
13057         var close = choice.select('a.select2-search-choice-close', true).first()
13058         
13059         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13060         
13061         this.item.push(o);
13062         
13063         this.lastData = o;
13064         
13065         this.syncValue();
13066         
13067         this.inputEl().dom.value = '';
13068         
13069         this.validate();
13070     },
13071     
13072     onRemoveItem : function(e, _self, o)
13073     {
13074         e.preventDefault();
13075         
13076         this.lastItem = Roo.apply([], this.item);
13077         
13078         var index = this.item.indexOf(o.data) * 1;
13079         
13080         if( index < 0){
13081             Roo.log('not this item?!');
13082             return;
13083         }
13084         
13085         this.item.splice(index, 1);
13086         o.item.remove();
13087         
13088         this.syncValue();
13089         
13090         this.fireEvent('remove', this, e);
13091         
13092         this.validate();
13093         
13094     },
13095     
13096     syncValue : function()
13097     {
13098         if(!this.item.length){
13099             this.clearValue();
13100             return;
13101         }
13102             
13103         var value = [];
13104         var _this = this;
13105         Roo.each(this.item, function(i){
13106             if(_this.valueField){
13107                 value.push(i[_this.valueField]);
13108                 return;
13109             }
13110
13111             value.push(i);
13112         });
13113
13114         this.value = value.join(',');
13115
13116         if(this.hiddenField){
13117             this.hiddenField.dom.value = this.value;
13118         }
13119         
13120         this.store.fireEvent("datachanged", this.store);
13121     },
13122     
13123     clearItem : function()
13124     {
13125         if(!this.multiple){
13126             return;
13127         }
13128         
13129         this.item = [];
13130         
13131         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13132            c.remove();
13133         });
13134         
13135         this.syncValue();
13136         
13137         this.validate();
13138         
13139         if(this.tickable && !Roo.isTouch){
13140             this.view.refresh();
13141         }
13142     },
13143     
13144     inputEl: function ()
13145     {
13146         if(Roo.isTouch && this.mobileTouchView){
13147             return this.el.select('input.form-control',true).first();
13148         }
13149         
13150         if(this.tickable){
13151             return this.searchField;
13152         }
13153         
13154         return this.el.select('input.form-control',true).first();
13155     },
13156     
13157     
13158     onTickableFooterButtonClick : function(e, btn, el)
13159     {
13160         e.preventDefault();
13161         
13162         this.lastItem = Roo.apply([], this.item);
13163         
13164         if(btn && btn.name == 'cancel'){
13165             this.tickItems = Roo.apply([], this.item);
13166             this.collapse();
13167             return;
13168         }
13169         
13170         this.clearItem();
13171         
13172         var _this = this;
13173         
13174         Roo.each(this.tickItems, function(o){
13175             _this.addItem(o);
13176         });
13177         
13178         this.collapse();
13179         
13180     },
13181     
13182     validate : function()
13183     {
13184         var v = this.getRawValue();
13185         
13186         if(this.multiple){
13187             v = this.getValue();
13188         }
13189         
13190         if(this.disabled || this.allowBlank || v.length){
13191             this.markValid();
13192             return true;
13193         }
13194         
13195         this.markInvalid();
13196         return false;
13197     },
13198     
13199     tickableInputEl : function()
13200     {
13201         if(!this.tickable || !this.editable){
13202             return this.inputEl();
13203         }
13204         
13205         return this.inputEl().select('.select2-search-field-input', true).first();
13206     },
13207     
13208     
13209     getAutoCreateTouchView : function()
13210     {
13211         var id = Roo.id();
13212         
13213         var cfg = {
13214             cls: 'form-group' //input-group
13215         };
13216         
13217         var input =  {
13218             tag: 'input',
13219             id : id,
13220             type : this.inputType,
13221             cls : 'form-control x-combo-noedit',
13222             autocomplete: 'new-password',
13223             placeholder : this.placeholder || '',
13224             readonly : true
13225         };
13226         
13227         if (this.name) {
13228             input.name = this.name;
13229         }
13230         
13231         if (this.size) {
13232             input.cls += ' input-' + this.size;
13233         }
13234         
13235         if (this.disabled) {
13236             input.disabled = true;
13237         }
13238         
13239         var inputblock = {
13240             cls : '',
13241             cn : [
13242                 input
13243             ]
13244         };
13245         
13246         if(this.before){
13247             inputblock.cls += ' input-group';
13248             
13249             inputblock.cn.unshift({
13250                 tag :'span',
13251                 cls : 'input-group-addon',
13252                 html : this.before
13253             });
13254         }
13255         
13256         if(this.removable && !this.multiple){
13257             inputblock.cls += ' roo-removable';
13258             
13259             inputblock.cn.push({
13260                 tag: 'button',
13261                 html : 'x',
13262                 cls : 'roo-combo-removable-btn close'
13263             });
13264         }
13265
13266         if(this.hasFeedback && !this.allowBlank){
13267             
13268             inputblock.cls += ' has-feedback';
13269             
13270             inputblock.cn.push({
13271                 tag: 'span',
13272                 cls: 'glyphicon form-control-feedback'
13273             });
13274             
13275         }
13276         
13277         if (this.after) {
13278             
13279             inputblock.cls += (this.before) ? '' : ' input-group';
13280             
13281             inputblock.cn.push({
13282                 tag :'span',
13283                 cls : 'input-group-addon',
13284                 html : this.after
13285             });
13286         }
13287
13288         var box = {
13289             tag: 'div',
13290             cn: [
13291                 {
13292                     tag: 'input',
13293                     type : 'hidden',
13294                     cls: 'form-hidden-field'
13295                 },
13296                 inputblock
13297             ]
13298             
13299         };
13300         
13301         if(this.multiple){
13302             box = {
13303                 tag: 'div',
13304                 cn: [
13305                     {
13306                         tag: 'input',
13307                         type : 'hidden',
13308                         cls: 'form-hidden-field'
13309                     },
13310                     {
13311                         tag: 'ul',
13312                         cls: 'select2-choices',
13313                         cn:[
13314                             {
13315                                 tag: 'li',
13316                                 cls: 'select2-search-field',
13317                                 cn: [
13318
13319                                     inputblock
13320                                 ]
13321                             }
13322                         ]
13323                     }
13324                 ]
13325             }
13326         };
13327         
13328         var combobox = {
13329             cls: 'select2-container input-group',
13330             cn: [
13331                 box
13332             ]
13333         };
13334         
13335         if(this.multiple){
13336             combobox.cls += ' select2-container-multi';
13337         }
13338         
13339         var align = this.labelAlign || this.parentLabelAlign();
13340         
13341         cfg.cn = combobox;
13342         
13343         if(this.fieldLabel.length){
13344             
13345             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13346             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13347             
13348             cfg.cn = [
13349                 {
13350                     tag: 'label',
13351                     cls : 'control-label ' + lw,
13352                     html : this.fieldLabel
13353
13354                 },
13355                 {
13356                     cls : cw, 
13357                     cn: [
13358                         combobox
13359                     ]
13360                 }
13361             ];
13362         }
13363         
13364         var settings = this;
13365         
13366         ['xs','sm','md','lg'].map(function(size){
13367             if (settings[size]) {
13368                 cfg.cls += ' col-' + size + '-' + settings[size];
13369             }
13370         });
13371         
13372         return cfg;
13373     },
13374     
13375     initTouchView : function()
13376     {
13377         this.renderTouchView();
13378         
13379         this.touchViewEl.on('scroll', function(){
13380             this.el.dom.scrollTop = 0;
13381         }, this);
13382         
13383         this.inputEl().on("click", this.showTouchView, this);
13384         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13385         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13386         
13387         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13388         
13389         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13390         this.store.on('load', this.onTouchViewLoad, this);
13391         this.store.on('loadexception', this.onTouchViewLoadException, this);
13392         
13393         if(this.hiddenName){
13394             
13395             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13396             
13397             this.hiddenField.dom.value =
13398                 this.hiddenValue !== undefined ? this.hiddenValue :
13399                 this.value !== undefined ? this.value : '';
13400         
13401             this.el.dom.removeAttribute('name');
13402             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13403         }
13404         
13405         if(this.multiple){
13406             this.choices = this.el.select('ul.select2-choices', true).first();
13407             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13408         }
13409         
13410         if(this.removable && !this.multiple){
13411             var close = this.closeTriggerEl();
13412             if(close){
13413                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13414                 close.on('click', this.removeBtnClick, this, close);
13415             }
13416         }
13417         
13418         return;
13419         
13420         
13421     },
13422     
13423     renderTouchView : function()
13424     {
13425         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13426         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13427         
13428         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13429         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13430         
13431         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13432         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13433         this.touchViewBodyEl.setStyle('overflow', 'auto');
13434         
13435         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13436         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13437         
13438         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13439         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13440         
13441     },
13442     
13443     showTouchView : function()
13444     {
13445         this.touchViewHeaderEl.hide();
13446
13447         if(this.fieldLabel.length){
13448             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13449             this.touchViewHeaderEl.show();
13450         }
13451
13452         this.touchViewEl.show();
13453
13454         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13455         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13456
13457         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13458
13459         if(this.fieldLabel.length){
13460             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13461         }
13462         
13463         this.touchViewBodyEl.setHeight(bodyHeight);
13464
13465         if(this.animate){
13466             var _this = this;
13467             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13468         }else{
13469             this.touchViewEl.addClass('in');
13470         }
13471
13472         this.doTouchViewQuery();
13473         
13474     },
13475     
13476     hideTouchView : function()
13477     {
13478         this.touchViewEl.removeClass('in');
13479
13480         if(this.animate){
13481             var _this = this;
13482             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13483         }else{
13484             this.touchViewEl.setStyle('display', 'none');
13485         }
13486         
13487     },
13488     
13489     setTouchViewValue : function()
13490     {
13491         if(this.multiple){
13492             this.clearItem();
13493         
13494             var _this = this;
13495
13496             Roo.each(this.tickItems, function(o){
13497                 this.addItem(o);
13498             }, this);
13499         }
13500         
13501         this.hideTouchView();
13502     },
13503     
13504     doTouchViewQuery : function()
13505     {
13506         var qe = {
13507             query: '',
13508             forceAll: true,
13509             combo: this,
13510             cancel:false
13511         };
13512         
13513         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13514             return false;
13515         }
13516         
13517         if(!this.alwaysQuery || this.mode == 'local'){
13518             this.onTouchViewLoad();
13519             return;
13520         }
13521         
13522         this.store.load();
13523     },
13524     
13525     onTouchViewBeforeLoad : function(combo,opts)
13526     {
13527         return;
13528     },
13529
13530     // private
13531     onTouchViewLoad : function()
13532     {
13533         if(this.store.getCount() < 1){
13534             this.onTouchViewEmptyResults();
13535             return;
13536         }
13537         
13538         this.clearTouchView();
13539         
13540         var rawValue = this.getRawValue();
13541         
13542         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13543         
13544         this.tickItems = [];
13545         
13546         this.store.data.each(function(d, rowIndex){
13547             var row = this.touchViewListGroup.createChild(template);
13548             
13549             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13550                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13551             }
13552             
13553             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13554                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13555             }
13556             
13557             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13558                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13559                 this.tickItems.push(d.data);
13560             }
13561             
13562             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13563             
13564         }, this);
13565         
13566         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13567         
13568         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13569
13570         if(this.fieldLabel.length){
13571             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13572         }
13573
13574         var listHeight = this.touchViewListGroup.getHeight();
13575         
13576         var _this = this;
13577         
13578         if(firstChecked && listHeight > bodyHeight){
13579             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13580         }
13581         
13582     },
13583     
13584     onTouchViewLoadException : function()
13585     {
13586         this.hideTouchView();
13587     },
13588     
13589     onTouchViewEmptyResults : function()
13590     {
13591         this.clearTouchView();
13592         
13593         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13594         
13595         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13596         
13597     },
13598     
13599     clearTouchView : function()
13600     {
13601         this.touchViewListGroup.dom.innerHTML = '';
13602     },
13603     
13604     onTouchViewClick : function(e, el, o)
13605     {
13606         e.preventDefault();
13607         
13608         var row = o.row;
13609         var rowIndex = o.rowIndex;
13610         
13611         var r = this.store.getAt(rowIndex);
13612         
13613         if(!this.multiple){
13614             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13615                 c.dom.removeAttribute('checked');
13616             }, this);
13617             
13618             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13619         
13620             this.setFromData(r.data);
13621             
13622             var close = this.closeTriggerEl();
13623         
13624             if(close){
13625                 close.show();
13626             }
13627
13628             this.hideTouchView();
13629             
13630             this.fireEvent('select', this, r, rowIndex);
13631             
13632             return;
13633         }
13634         
13635         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13636             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13637             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13638             return;
13639         }
13640         
13641         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13642         this.addItem(r.data);
13643         this.tickItems.push(r.data);
13644         
13645     }
13646     
13647
13648     /** 
13649     * @cfg {Boolean} grow 
13650     * @hide 
13651     */
13652     /** 
13653     * @cfg {Number} growMin 
13654     * @hide 
13655     */
13656     /** 
13657     * @cfg {Number} growMax 
13658     * @hide 
13659     */
13660     /**
13661      * @hide
13662      * @method autoSize
13663      */
13664 });
13665
13666 Roo.apply(Roo.bootstrap.ComboBox,  {
13667     
13668     header : {
13669         tag: 'div',
13670         cls: 'modal-header',
13671         cn: [
13672             {
13673                 tag: 'h4',
13674                 cls: 'modal-title'
13675             }
13676         ]
13677     },
13678     
13679     body : {
13680         tag: 'div',
13681         cls: 'modal-body',
13682         cn: [
13683             {
13684                 tag: 'ul',
13685                 cls: 'list-group'
13686             }
13687         ]
13688     },
13689     
13690     listItemRadio : {
13691         tag: 'li',
13692         cls: 'list-group-item',
13693         cn: [
13694             {
13695                 tag: 'span',
13696                 cls: 'roo-combobox-list-group-item-value'
13697             },
13698             {
13699                 tag: 'div',
13700                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13701                 cn: [
13702                     {
13703                         tag: 'input',
13704                         type: 'radio'
13705                     },
13706                     {
13707                         tag: 'label'
13708                     }
13709                 ]
13710             }
13711         ]
13712     },
13713     
13714     listItemCheckbox : {
13715         tag: 'li',
13716         cls: 'list-group-item',
13717         cn: [
13718             {
13719                 tag: 'span',
13720                 cls: 'roo-combobox-list-group-item-value'
13721             },
13722             {
13723                 tag: 'div',
13724                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13725                 cn: [
13726                     {
13727                         tag: 'input',
13728                         type: 'checkbox'
13729                     },
13730                     {
13731                         tag: 'label'
13732                     }
13733                 ]
13734             }
13735         ]
13736     },
13737     
13738     emptyResult : {
13739         tag: 'div',
13740         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13741     },
13742     
13743     footer : {
13744         tag: 'div',
13745         cls: 'modal-footer',
13746         cn: [
13747             {
13748                 tag: 'div',
13749                 cls: 'row',
13750                 cn: [
13751                     {
13752                         tag: 'div',
13753                         cls: 'col-xs-6 text-left',
13754                         cn: {
13755                             tag: 'button',
13756                             cls: 'btn btn-danger roo-touch-view-cancel',
13757                             html: 'Cancel'
13758                         }
13759                     },
13760                     {
13761                         tag: 'div',
13762                         cls: 'col-xs-6 text-right',
13763                         cn: {
13764                             tag: 'button',
13765                             cls: 'btn btn-success roo-touch-view-ok',
13766                             html: 'OK'
13767                         }
13768                     }
13769                 ]
13770             }
13771         ]
13772         
13773     }
13774 });
13775
13776 Roo.apply(Roo.bootstrap.ComboBox,  {
13777     
13778     touchViewTemplate : {
13779         tag: 'div',
13780         cls: 'modal fade roo-combobox-touch-view',
13781         cn: [
13782             {
13783                 tag: 'div',
13784                 cls: 'modal-dialog',
13785                 cn: [
13786                     {
13787                         tag: 'div',
13788                         cls: 'modal-content',
13789                         cn: [
13790                             Roo.bootstrap.ComboBox.header,
13791                             Roo.bootstrap.ComboBox.body,
13792                             Roo.bootstrap.ComboBox.footer
13793                         ]
13794                     }
13795                 ]
13796             }
13797         ]
13798     }
13799 });/*
13800  * Based on:
13801  * Ext JS Library 1.1.1
13802  * Copyright(c) 2006-2007, Ext JS, LLC.
13803  *
13804  * Originally Released Under LGPL - original licence link has changed is not relivant.
13805  *
13806  * Fork - LGPL
13807  * <script type="text/javascript">
13808  */
13809
13810 /**
13811  * @class Roo.View
13812  * @extends Roo.util.Observable
13813  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13814  * This class also supports single and multi selection modes. <br>
13815  * Create a data model bound view:
13816  <pre><code>
13817  var store = new Roo.data.Store(...);
13818
13819  var view = new Roo.View({
13820     el : "my-element",
13821     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13822  
13823     singleSelect: true,
13824     selectedClass: "ydataview-selected",
13825     store: store
13826  });
13827
13828  // listen for node click?
13829  view.on("click", function(vw, index, node, e){
13830  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13831  });
13832
13833  // load XML data
13834  dataModel.load("foobar.xml");
13835  </code></pre>
13836  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13837  * <br><br>
13838  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13839  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13840  * 
13841  * Note: old style constructor is still suported (container, template, config)
13842  * 
13843  * @constructor
13844  * Create a new View
13845  * @param {Object} config The config object
13846  * 
13847  */
13848 Roo.View = function(config, depreciated_tpl, depreciated_config){
13849     
13850     this.parent = false;
13851     
13852     if (typeof(depreciated_tpl) == 'undefined') {
13853         // new way.. - universal constructor.
13854         Roo.apply(this, config);
13855         this.el  = Roo.get(this.el);
13856     } else {
13857         // old format..
13858         this.el  = Roo.get(config);
13859         this.tpl = depreciated_tpl;
13860         Roo.apply(this, depreciated_config);
13861     }
13862     this.wrapEl  = this.el.wrap().wrap();
13863     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13864     
13865     
13866     if(typeof(this.tpl) == "string"){
13867         this.tpl = new Roo.Template(this.tpl);
13868     } else {
13869         // support xtype ctors..
13870         this.tpl = new Roo.factory(this.tpl, Roo);
13871     }
13872     
13873     
13874     this.tpl.compile();
13875     
13876     /** @private */
13877     this.addEvents({
13878         /**
13879          * @event beforeclick
13880          * Fires before a click is processed. Returns false to cancel the default action.
13881          * @param {Roo.View} this
13882          * @param {Number} index The index of the target node
13883          * @param {HTMLElement} node The target node
13884          * @param {Roo.EventObject} e The raw event object
13885          */
13886             "beforeclick" : true,
13887         /**
13888          * @event click
13889          * Fires when a template node is clicked.
13890          * @param {Roo.View} this
13891          * @param {Number} index The index of the target node
13892          * @param {HTMLElement} node The target node
13893          * @param {Roo.EventObject} e The raw event object
13894          */
13895             "click" : true,
13896         /**
13897          * @event dblclick
13898          * Fires when a template node is double clicked.
13899          * @param {Roo.View} this
13900          * @param {Number} index The index of the target node
13901          * @param {HTMLElement} node The target node
13902          * @param {Roo.EventObject} e The raw event object
13903          */
13904             "dblclick" : true,
13905         /**
13906          * @event contextmenu
13907          * Fires when a template node is right clicked.
13908          * @param {Roo.View} this
13909          * @param {Number} index The index of the target node
13910          * @param {HTMLElement} node The target node
13911          * @param {Roo.EventObject} e The raw event object
13912          */
13913             "contextmenu" : true,
13914         /**
13915          * @event selectionchange
13916          * Fires when the selected nodes change.
13917          * @param {Roo.View} this
13918          * @param {Array} selections Array of the selected nodes
13919          */
13920             "selectionchange" : true,
13921     
13922         /**
13923          * @event beforeselect
13924          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13925          * @param {Roo.View} this
13926          * @param {HTMLElement} node The node to be selected
13927          * @param {Array} selections Array of currently selected nodes
13928          */
13929             "beforeselect" : true,
13930         /**
13931          * @event preparedata
13932          * Fires on every row to render, to allow you to change the data.
13933          * @param {Roo.View} this
13934          * @param {Object} data to be rendered (change this)
13935          */
13936           "preparedata" : true
13937           
13938           
13939         });
13940
13941
13942
13943     this.el.on({
13944         "click": this.onClick,
13945         "dblclick": this.onDblClick,
13946         "contextmenu": this.onContextMenu,
13947         scope:this
13948     });
13949
13950     this.selections = [];
13951     this.nodes = [];
13952     this.cmp = new Roo.CompositeElementLite([]);
13953     if(this.store){
13954         this.store = Roo.factory(this.store, Roo.data);
13955         this.setStore(this.store, true);
13956     }
13957     
13958     if ( this.footer && this.footer.xtype) {
13959            
13960          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13961         
13962         this.footer.dataSource = this.store;
13963         this.footer.container = fctr;
13964         this.footer = Roo.factory(this.footer, Roo);
13965         fctr.insertFirst(this.el);
13966         
13967         // this is a bit insane - as the paging toolbar seems to detach the el..
13968 //        dom.parentNode.parentNode.parentNode
13969          // they get detached?
13970     }
13971     
13972     
13973     Roo.View.superclass.constructor.call(this);
13974     
13975     
13976 };
13977
13978 Roo.extend(Roo.View, Roo.util.Observable, {
13979     
13980      /**
13981      * @cfg {Roo.data.Store} store Data store to load data from.
13982      */
13983     store : false,
13984     
13985     /**
13986      * @cfg {String|Roo.Element} el The container element.
13987      */
13988     el : '',
13989     
13990     /**
13991      * @cfg {String|Roo.Template} tpl The template used by this View 
13992      */
13993     tpl : false,
13994     /**
13995      * @cfg {String} dataName the named area of the template to use as the data area
13996      *                          Works with domtemplates roo-name="name"
13997      */
13998     dataName: false,
13999     /**
14000      * @cfg {String} selectedClass The css class to add to selected nodes
14001      */
14002     selectedClass : "x-view-selected",
14003      /**
14004      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14005      */
14006     emptyText : "",
14007     
14008     /**
14009      * @cfg {String} text to display on mask (default Loading)
14010      */
14011     mask : false,
14012     /**
14013      * @cfg {Boolean} multiSelect Allow multiple selection
14014      */
14015     multiSelect : false,
14016     /**
14017      * @cfg {Boolean} singleSelect Allow single selection
14018      */
14019     singleSelect:  false,
14020     
14021     /**
14022      * @cfg {Boolean} toggleSelect - selecting 
14023      */
14024     toggleSelect : false,
14025     
14026     /**
14027      * @cfg {Boolean} tickable - selecting 
14028      */
14029     tickable : false,
14030     
14031     /**
14032      * Returns the element this view is bound to.
14033      * @return {Roo.Element}
14034      */
14035     getEl : function(){
14036         return this.wrapEl;
14037     },
14038     
14039     
14040
14041     /**
14042      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14043      */
14044     refresh : function(){
14045         //Roo.log('refresh');
14046         var t = this.tpl;
14047         
14048         // if we are using something like 'domtemplate', then
14049         // the what gets used is:
14050         // t.applySubtemplate(NAME, data, wrapping data..)
14051         // the outer template then get' applied with
14052         //     the store 'extra data'
14053         // and the body get's added to the
14054         //      roo-name="data" node?
14055         //      <span class='roo-tpl-{name}'></span> ?????
14056         
14057         
14058         
14059         this.clearSelections();
14060         this.el.update("");
14061         var html = [];
14062         var records = this.store.getRange();
14063         if(records.length < 1) {
14064             
14065             // is this valid??  = should it render a template??
14066             
14067             this.el.update(this.emptyText);
14068             return;
14069         }
14070         var el = this.el;
14071         if (this.dataName) {
14072             this.el.update(t.apply(this.store.meta)); //????
14073             el = this.el.child('.roo-tpl-' + this.dataName);
14074         }
14075         
14076         for(var i = 0, len = records.length; i < len; i++){
14077             var data = this.prepareData(records[i].data, i, records[i]);
14078             this.fireEvent("preparedata", this, data, i, records[i]);
14079             
14080             var d = Roo.apply({}, data);
14081             
14082             if(this.tickable){
14083                 Roo.apply(d, {'roo-id' : Roo.id()});
14084                 
14085                 var _this = this;
14086             
14087                 Roo.each(this.parent.item, function(item){
14088                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14089                         return;
14090                     }
14091                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14092                 });
14093             }
14094             
14095             html[html.length] = Roo.util.Format.trim(
14096                 this.dataName ?
14097                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14098                     t.apply(d)
14099             );
14100         }
14101         
14102         
14103         
14104         el.update(html.join(""));
14105         this.nodes = el.dom.childNodes;
14106         this.updateIndexes(0);
14107     },
14108     
14109
14110     /**
14111      * Function to override to reformat the data that is sent to
14112      * the template for each node.
14113      * DEPRICATED - use the preparedata event handler.
14114      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14115      * a JSON object for an UpdateManager bound view).
14116      */
14117     prepareData : function(data, index, record)
14118     {
14119         this.fireEvent("preparedata", this, data, index, record);
14120         return data;
14121     },
14122
14123     onUpdate : function(ds, record){
14124         // Roo.log('on update');   
14125         this.clearSelections();
14126         var index = this.store.indexOf(record);
14127         var n = this.nodes[index];
14128         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14129         n.parentNode.removeChild(n);
14130         this.updateIndexes(index, index);
14131     },
14132
14133     
14134     
14135 // --------- FIXME     
14136     onAdd : function(ds, records, index)
14137     {
14138         //Roo.log(['on Add', ds, records, index] );        
14139         this.clearSelections();
14140         if(this.nodes.length == 0){
14141             this.refresh();
14142             return;
14143         }
14144         var n = this.nodes[index];
14145         for(var i = 0, len = records.length; i < len; i++){
14146             var d = this.prepareData(records[i].data, i, records[i]);
14147             if(n){
14148                 this.tpl.insertBefore(n, d);
14149             }else{
14150                 
14151                 this.tpl.append(this.el, d);
14152             }
14153         }
14154         this.updateIndexes(index);
14155     },
14156
14157     onRemove : function(ds, record, index){
14158        // Roo.log('onRemove');
14159         this.clearSelections();
14160         var el = this.dataName  ?
14161             this.el.child('.roo-tpl-' + this.dataName) :
14162             this.el; 
14163         
14164         el.dom.removeChild(this.nodes[index]);
14165         this.updateIndexes(index);
14166     },
14167
14168     /**
14169      * Refresh an individual node.
14170      * @param {Number} index
14171      */
14172     refreshNode : function(index){
14173         this.onUpdate(this.store, this.store.getAt(index));
14174     },
14175
14176     updateIndexes : function(startIndex, endIndex){
14177         var ns = this.nodes;
14178         startIndex = startIndex || 0;
14179         endIndex = endIndex || ns.length - 1;
14180         for(var i = startIndex; i <= endIndex; i++){
14181             ns[i].nodeIndex = i;
14182         }
14183     },
14184
14185     /**
14186      * Changes the data store this view uses and refresh the view.
14187      * @param {Store} store
14188      */
14189     setStore : function(store, initial){
14190         if(!initial && this.store){
14191             this.store.un("datachanged", this.refresh);
14192             this.store.un("add", this.onAdd);
14193             this.store.un("remove", this.onRemove);
14194             this.store.un("update", this.onUpdate);
14195             this.store.un("clear", this.refresh);
14196             this.store.un("beforeload", this.onBeforeLoad);
14197             this.store.un("load", this.onLoad);
14198             this.store.un("loadexception", this.onLoad);
14199         }
14200         if(store){
14201           
14202             store.on("datachanged", this.refresh, this);
14203             store.on("add", this.onAdd, this);
14204             store.on("remove", this.onRemove, this);
14205             store.on("update", this.onUpdate, this);
14206             store.on("clear", this.refresh, this);
14207             store.on("beforeload", this.onBeforeLoad, this);
14208             store.on("load", this.onLoad, this);
14209             store.on("loadexception", this.onLoad, this);
14210         }
14211         
14212         if(store){
14213             this.refresh();
14214         }
14215     },
14216     /**
14217      * onbeforeLoad - masks the loading area.
14218      *
14219      */
14220     onBeforeLoad : function(store,opts)
14221     {
14222          //Roo.log('onBeforeLoad');   
14223         if (!opts.add) {
14224             this.el.update("");
14225         }
14226         this.el.mask(this.mask ? this.mask : "Loading" ); 
14227     },
14228     onLoad : function ()
14229     {
14230         this.el.unmask();
14231     },
14232     
14233
14234     /**
14235      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14236      * @param {HTMLElement} node
14237      * @return {HTMLElement} The template node
14238      */
14239     findItemFromChild : function(node){
14240         var el = this.dataName  ?
14241             this.el.child('.roo-tpl-' + this.dataName,true) :
14242             this.el.dom; 
14243         
14244         if(!node || node.parentNode == el){
14245                     return node;
14246             }
14247             var p = node.parentNode;
14248             while(p && p != el){
14249             if(p.parentNode == el){
14250                 return p;
14251             }
14252             p = p.parentNode;
14253         }
14254             return null;
14255     },
14256
14257     /** @ignore */
14258     onClick : function(e){
14259         var item = this.findItemFromChild(e.getTarget());
14260         if(item){
14261             var index = this.indexOf(item);
14262             if(this.onItemClick(item, index, e) !== false){
14263                 this.fireEvent("click", this, index, item, e);
14264             }
14265         }else{
14266             this.clearSelections();
14267         }
14268     },
14269
14270     /** @ignore */
14271     onContextMenu : function(e){
14272         var item = this.findItemFromChild(e.getTarget());
14273         if(item){
14274             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14275         }
14276     },
14277
14278     /** @ignore */
14279     onDblClick : function(e){
14280         var item = this.findItemFromChild(e.getTarget());
14281         if(item){
14282             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14283         }
14284     },
14285
14286     onItemClick : function(item, index, e)
14287     {
14288         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14289             return false;
14290         }
14291         if (this.toggleSelect) {
14292             var m = this.isSelected(item) ? 'unselect' : 'select';
14293             //Roo.log(m);
14294             var _t = this;
14295             _t[m](item, true, false);
14296             return true;
14297         }
14298         if(this.multiSelect || this.singleSelect){
14299             if(this.multiSelect && e.shiftKey && this.lastSelection){
14300                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14301             }else{
14302                 this.select(item, this.multiSelect && e.ctrlKey);
14303                 this.lastSelection = item;
14304             }
14305             
14306             if(!this.tickable){
14307                 e.preventDefault();
14308             }
14309             
14310         }
14311         return true;
14312     },
14313
14314     /**
14315      * Get the number of selected nodes.
14316      * @return {Number}
14317      */
14318     getSelectionCount : function(){
14319         return this.selections.length;
14320     },
14321
14322     /**
14323      * Get the currently selected nodes.
14324      * @return {Array} An array of HTMLElements
14325      */
14326     getSelectedNodes : function(){
14327         return this.selections;
14328     },
14329
14330     /**
14331      * Get the indexes of the selected nodes.
14332      * @return {Array}
14333      */
14334     getSelectedIndexes : function(){
14335         var indexes = [], s = this.selections;
14336         for(var i = 0, len = s.length; i < len; i++){
14337             indexes.push(s[i].nodeIndex);
14338         }
14339         return indexes;
14340     },
14341
14342     /**
14343      * Clear all selections
14344      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14345      */
14346     clearSelections : function(suppressEvent){
14347         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14348             this.cmp.elements = this.selections;
14349             this.cmp.removeClass(this.selectedClass);
14350             this.selections = [];
14351             if(!suppressEvent){
14352                 this.fireEvent("selectionchange", this, this.selections);
14353             }
14354         }
14355     },
14356
14357     /**
14358      * Returns true if the passed node is selected
14359      * @param {HTMLElement/Number} node The node or node index
14360      * @return {Boolean}
14361      */
14362     isSelected : function(node){
14363         var s = this.selections;
14364         if(s.length < 1){
14365             return false;
14366         }
14367         node = this.getNode(node);
14368         return s.indexOf(node) !== -1;
14369     },
14370
14371     /**
14372      * Selects nodes.
14373      * @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
14374      * @param {Boolean} keepExisting (optional) true to keep existing selections
14375      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14376      */
14377     select : function(nodeInfo, keepExisting, suppressEvent){
14378         if(nodeInfo instanceof Array){
14379             if(!keepExisting){
14380                 this.clearSelections(true);
14381             }
14382             for(var i = 0, len = nodeInfo.length; i < len; i++){
14383                 this.select(nodeInfo[i], true, true);
14384             }
14385             return;
14386         } 
14387         var node = this.getNode(nodeInfo);
14388         if(!node || this.isSelected(node)){
14389             return; // already selected.
14390         }
14391         if(!keepExisting){
14392             this.clearSelections(true);
14393         }
14394         
14395         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14396             Roo.fly(node).addClass(this.selectedClass);
14397             this.selections.push(node);
14398             if(!suppressEvent){
14399                 this.fireEvent("selectionchange", this, this.selections);
14400             }
14401         }
14402         
14403         
14404     },
14405       /**
14406      * Unselects nodes.
14407      * @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
14408      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14409      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14410      */
14411     unselect : function(nodeInfo, keepExisting, suppressEvent)
14412     {
14413         if(nodeInfo instanceof Array){
14414             Roo.each(this.selections, function(s) {
14415                 this.unselect(s, nodeInfo);
14416             }, this);
14417             return;
14418         }
14419         var node = this.getNode(nodeInfo);
14420         if(!node || !this.isSelected(node)){
14421             //Roo.log("not selected");
14422             return; // not selected.
14423         }
14424         // fireevent???
14425         var ns = [];
14426         Roo.each(this.selections, function(s) {
14427             if (s == node ) {
14428                 Roo.fly(node).removeClass(this.selectedClass);
14429
14430                 return;
14431             }
14432             ns.push(s);
14433         },this);
14434         
14435         this.selections= ns;
14436         this.fireEvent("selectionchange", this, this.selections);
14437     },
14438
14439     /**
14440      * Gets a template node.
14441      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14442      * @return {HTMLElement} The node or null if it wasn't found
14443      */
14444     getNode : function(nodeInfo){
14445         if(typeof nodeInfo == "string"){
14446             return document.getElementById(nodeInfo);
14447         }else if(typeof nodeInfo == "number"){
14448             return this.nodes[nodeInfo];
14449         }
14450         return nodeInfo;
14451     },
14452
14453     /**
14454      * Gets a range template nodes.
14455      * @param {Number} startIndex
14456      * @param {Number} endIndex
14457      * @return {Array} An array of nodes
14458      */
14459     getNodes : function(start, end){
14460         var ns = this.nodes;
14461         start = start || 0;
14462         end = typeof end == "undefined" ? ns.length - 1 : end;
14463         var nodes = [];
14464         if(start <= end){
14465             for(var i = start; i <= end; i++){
14466                 nodes.push(ns[i]);
14467             }
14468         } else{
14469             for(var i = start; i >= end; i--){
14470                 nodes.push(ns[i]);
14471             }
14472         }
14473         return nodes;
14474     },
14475
14476     /**
14477      * Finds the index of the passed node
14478      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14479      * @return {Number} The index of the node or -1
14480      */
14481     indexOf : function(node){
14482         node = this.getNode(node);
14483         if(typeof node.nodeIndex == "number"){
14484             return node.nodeIndex;
14485         }
14486         var ns = this.nodes;
14487         for(var i = 0, len = ns.length; i < len; i++){
14488             if(ns[i] == node){
14489                 return i;
14490             }
14491         }
14492         return -1;
14493     }
14494 });
14495 /*
14496  * - LGPL
14497  *
14498  * based on jquery fullcalendar
14499  * 
14500  */
14501
14502 Roo.bootstrap = Roo.bootstrap || {};
14503 /**
14504  * @class Roo.bootstrap.Calendar
14505  * @extends Roo.bootstrap.Component
14506  * Bootstrap Calendar class
14507  * @cfg {Boolean} loadMask (true|false) default false
14508  * @cfg {Object} header generate the user specific header of the calendar, default false
14509
14510  * @constructor
14511  * Create a new Container
14512  * @param {Object} config The config object
14513  */
14514
14515
14516
14517 Roo.bootstrap.Calendar = function(config){
14518     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14519      this.addEvents({
14520         /**
14521              * @event select
14522              * Fires when a date is selected
14523              * @param {DatePicker} this
14524              * @param {Date} date The selected date
14525              */
14526         'select': true,
14527         /**
14528              * @event monthchange
14529              * Fires when the displayed month changes 
14530              * @param {DatePicker} this
14531              * @param {Date} date The selected month
14532              */
14533         'monthchange': true,
14534         /**
14535              * @event evententer
14536              * Fires when mouse over an event
14537              * @param {Calendar} this
14538              * @param {event} Event
14539              */
14540         'evententer': true,
14541         /**
14542              * @event eventleave
14543              * Fires when the mouse leaves an
14544              * @param {Calendar} this
14545              * @param {event}
14546              */
14547         'eventleave': true,
14548         /**
14549              * @event eventclick
14550              * Fires when the mouse click an
14551              * @param {Calendar} this
14552              * @param {event}
14553              */
14554         'eventclick': true
14555         
14556     });
14557
14558 };
14559
14560 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14561     
14562      /**
14563      * @cfg {Number} startDay
14564      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14565      */
14566     startDay : 0,
14567     
14568     loadMask : false,
14569     
14570     header : false,
14571       
14572     getAutoCreate : function(){
14573         
14574         
14575         var fc_button = function(name, corner, style, content ) {
14576             return Roo.apply({},{
14577                 tag : 'span',
14578                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14579                          (corner.length ?
14580                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14581                             ''
14582                         ),
14583                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14584                 unselectable: 'on'
14585             });
14586         };
14587         
14588         var header = {};
14589         
14590         if(!this.header){
14591             header = {
14592                 tag : 'table',
14593                 cls : 'fc-header',
14594                 style : 'width:100%',
14595                 cn : [
14596                     {
14597                         tag: 'tr',
14598                         cn : [
14599                             {
14600                                 tag : 'td',
14601                                 cls : 'fc-header-left',
14602                                 cn : [
14603                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14604                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14605                                     { tag: 'span', cls: 'fc-header-space' },
14606                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14607
14608
14609                                 ]
14610                             },
14611
14612                             {
14613                                 tag : 'td',
14614                                 cls : 'fc-header-center',
14615                                 cn : [
14616                                     {
14617                                         tag: 'span',
14618                                         cls: 'fc-header-title',
14619                                         cn : {
14620                                             tag: 'H2',
14621                                             html : 'month / year'
14622                                         }
14623                                     }
14624
14625                                 ]
14626                             },
14627                             {
14628                                 tag : 'td',
14629                                 cls : 'fc-header-right',
14630                                 cn : [
14631                               /*      fc_button('month', 'left', '', 'month' ),
14632                                     fc_button('week', '', '', 'week' ),
14633                                     fc_button('day', 'right', '', 'day' )
14634                                 */    
14635
14636                                 ]
14637                             }
14638
14639                         ]
14640                     }
14641                 ]
14642             };
14643         }
14644         
14645         header = this.header;
14646         
14647        
14648         var cal_heads = function() {
14649             var ret = [];
14650             // fixme - handle this.
14651             
14652             for (var i =0; i < Date.dayNames.length; i++) {
14653                 var d = Date.dayNames[i];
14654                 ret.push({
14655                     tag: 'th',
14656                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14657                     html : d.substring(0,3)
14658                 });
14659                 
14660             }
14661             ret[0].cls += ' fc-first';
14662             ret[6].cls += ' fc-last';
14663             return ret;
14664         };
14665         var cal_cell = function(n) {
14666             return  {
14667                 tag: 'td',
14668                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14669                 cn : [
14670                     {
14671                         cn : [
14672                             {
14673                                 cls: 'fc-day-number',
14674                                 html: 'D'
14675                             },
14676                             {
14677                                 cls: 'fc-day-content',
14678                              
14679                                 cn : [
14680                                      {
14681                                         style: 'position: relative;' // height: 17px;
14682                                     }
14683                                 ]
14684                             }
14685                             
14686                             
14687                         ]
14688                     }
14689                 ]
14690                 
14691             }
14692         };
14693         var cal_rows = function() {
14694             
14695             var ret = [];
14696             for (var r = 0; r < 6; r++) {
14697                 var row= {
14698                     tag : 'tr',
14699                     cls : 'fc-week',
14700                     cn : []
14701                 };
14702                 
14703                 for (var i =0; i < Date.dayNames.length; i++) {
14704                     var d = Date.dayNames[i];
14705                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14706
14707                 }
14708                 row.cn[0].cls+=' fc-first';
14709                 row.cn[0].cn[0].style = 'min-height:90px';
14710                 row.cn[6].cls+=' fc-last';
14711                 ret.push(row);
14712                 
14713             }
14714             ret[0].cls += ' fc-first';
14715             ret[4].cls += ' fc-prev-last';
14716             ret[5].cls += ' fc-last';
14717             return ret;
14718             
14719         };
14720         
14721         var cal_table = {
14722             tag: 'table',
14723             cls: 'fc-border-separate',
14724             style : 'width:100%',
14725             cellspacing  : 0,
14726             cn : [
14727                 { 
14728                     tag: 'thead',
14729                     cn : [
14730                         { 
14731                             tag: 'tr',
14732                             cls : 'fc-first fc-last',
14733                             cn : cal_heads()
14734                         }
14735                     ]
14736                 },
14737                 { 
14738                     tag: 'tbody',
14739                     cn : cal_rows()
14740                 }
14741                   
14742             ]
14743         };
14744          
14745          var cfg = {
14746             cls : 'fc fc-ltr',
14747             cn : [
14748                 header,
14749                 {
14750                     cls : 'fc-content',
14751                     style : "position: relative;",
14752                     cn : [
14753                         {
14754                             cls : 'fc-view fc-view-month fc-grid',
14755                             style : 'position: relative',
14756                             unselectable : 'on',
14757                             cn : [
14758                                 {
14759                                     cls : 'fc-event-container',
14760                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14761                                 },
14762                                 cal_table
14763                             ]
14764                         }
14765                     ]
14766     
14767                 }
14768            ] 
14769             
14770         };
14771         
14772          
14773         
14774         return cfg;
14775     },
14776     
14777     
14778     initEvents : function()
14779     {
14780         if(!this.store){
14781             throw "can not find store for calendar";
14782         }
14783         
14784         var mark = {
14785             tag: "div",
14786             cls:"x-dlg-mask",
14787             style: "text-align:center",
14788             cn: [
14789                 {
14790                     tag: "div",
14791                     style: "background-color:white;width:50%;margin:250 auto",
14792                     cn: [
14793                         {
14794                             tag: "img",
14795                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14796                         },
14797                         {
14798                             tag: "span",
14799                             html: "Loading"
14800                         }
14801                         
14802                     ]
14803                 }
14804             ]
14805         }
14806         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14807         
14808         var size = this.el.select('.fc-content', true).first().getSize();
14809         this.maskEl.setSize(size.width, size.height);
14810         this.maskEl.enableDisplayMode("block");
14811         if(!this.loadMask){
14812             this.maskEl.hide();
14813         }
14814         
14815         this.store = Roo.factory(this.store, Roo.data);
14816         this.store.on('load', this.onLoad, this);
14817         this.store.on('beforeload', this.onBeforeLoad, this);
14818         
14819         this.resize();
14820         
14821         this.cells = this.el.select('.fc-day',true);
14822         //Roo.log(this.cells);
14823         this.textNodes = this.el.query('.fc-day-number');
14824         this.cells.addClassOnOver('fc-state-hover');
14825         
14826         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14827         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14828         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14829         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14830         
14831         this.on('monthchange', this.onMonthChange, this);
14832         
14833         this.update(new Date().clearTime());
14834     },
14835     
14836     resize : function() {
14837         var sz  = this.el.getSize();
14838         
14839         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14840         this.el.select('.fc-day-content div',true).setHeight(34);
14841     },
14842     
14843     
14844     // private
14845     showPrevMonth : function(e){
14846         this.update(this.activeDate.add("mo", -1));
14847     },
14848     showToday : function(e){
14849         this.update(new Date().clearTime());
14850     },
14851     // private
14852     showNextMonth : function(e){
14853         this.update(this.activeDate.add("mo", 1));
14854     },
14855
14856     // private
14857     showPrevYear : function(){
14858         this.update(this.activeDate.add("y", -1));
14859     },
14860
14861     // private
14862     showNextYear : function(){
14863         this.update(this.activeDate.add("y", 1));
14864     },
14865
14866     
14867    // private
14868     update : function(date)
14869     {
14870         var vd = this.activeDate;
14871         this.activeDate = date;
14872 //        if(vd && this.el){
14873 //            var t = date.getTime();
14874 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14875 //                Roo.log('using add remove');
14876 //                
14877 //                this.fireEvent('monthchange', this, date);
14878 //                
14879 //                this.cells.removeClass("fc-state-highlight");
14880 //                this.cells.each(function(c){
14881 //                   if(c.dateValue == t){
14882 //                       c.addClass("fc-state-highlight");
14883 //                       setTimeout(function(){
14884 //                            try{c.dom.firstChild.focus();}catch(e){}
14885 //                       }, 50);
14886 //                       return false;
14887 //                   }
14888 //                   return true;
14889 //                });
14890 //                return;
14891 //            }
14892 //        }
14893         
14894         var days = date.getDaysInMonth();
14895         
14896         var firstOfMonth = date.getFirstDateOfMonth();
14897         var startingPos = firstOfMonth.getDay()-this.startDay;
14898         
14899         if(startingPos < this.startDay){
14900             startingPos += 7;
14901         }
14902         
14903         var pm = date.add(Date.MONTH, -1);
14904         var prevStart = pm.getDaysInMonth()-startingPos;
14905 //        
14906         this.cells = this.el.select('.fc-day',true);
14907         this.textNodes = this.el.query('.fc-day-number');
14908         this.cells.addClassOnOver('fc-state-hover');
14909         
14910         var cells = this.cells.elements;
14911         var textEls = this.textNodes;
14912         
14913         Roo.each(cells, function(cell){
14914             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14915         });
14916         
14917         days += startingPos;
14918
14919         // convert everything to numbers so it's fast
14920         var day = 86400000;
14921         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14922         //Roo.log(d);
14923         //Roo.log(pm);
14924         //Roo.log(prevStart);
14925         
14926         var today = new Date().clearTime().getTime();
14927         var sel = date.clearTime().getTime();
14928         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14929         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14930         var ddMatch = this.disabledDatesRE;
14931         var ddText = this.disabledDatesText;
14932         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14933         var ddaysText = this.disabledDaysText;
14934         var format = this.format;
14935         
14936         var setCellClass = function(cal, cell){
14937             cell.row = 0;
14938             cell.events = [];
14939             cell.more = [];
14940             //Roo.log('set Cell Class');
14941             cell.title = "";
14942             var t = d.getTime();
14943             
14944             //Roo.log(d);
14945             
14946             cell.dateValue = t;
14947             if(t == today){
14948                 cell.className += " fc-today";
14949                 cell.className += " fc-state-highlight";
14950                 cell.title = cal.todayText;
14951             }
14952             if(t == sel){
14953                 // disable highlight in other month..
14954                 //cell.className += " fc-state-highlight";
14955                 
14956             }
14957             // disabling
14958             if(t < min) {
14959                 cell.className = " fc-state-disabled";
14960                 cell.title = cal.minText;
14961                 return;
14962             }
14963             if(t > max) {
14964                 cell.className = " fc-state-disabled";
14965                 cell.title = cal.maxText;
14966                 return;
14967             }
14968             if(ddays){
14969                 if(ddays.indexOf(d.getDay()) != -1){
14970                     cell.title = ddaysText;
14971                     cell.className = " fc-state-disabled";
14972                 }
14973             }
14974             if(ddMatch && format){
14975                 var fvalue = d.dateFormat(format);
14976                 if(ddMatch.test(fvalue)){
14977                     cell.title = ddText.replace("%0", fvalue);
14978                     cell.className = " fc-state-disabled";
14979                 }
14980             }
14981             
14982             if (!cell.initialClassName) {
14983                 cell.initialClassName = cell.dom.className;
14984             }
14985             
14986             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14987         };
14988
14989         var i = 0;
14990         
14991         for(; i < startingPos; i++) {
14992             textEls[i].innerHTML = (++prevStart);
14993             d.setDate(d.getDate()+1);
14994             
14995             cells[i].className = "fc-past fc-other-month";
14996             setCellClass(this, cells[i]);
14997         }
14998         
14999         var intDay = 0;
15000         
15001         for(; i < days; i++){
15002             intDay = i - startingPos + 1;
15003             textEls[i].innerHTML = (intDay);
15004             d.setDate(d.getDate()+1);
15005             
15006             cells[i].className = ''; // "x-date-active";
15007             setCellClass(this, cells[i]);
15008         }
15009         var extraDays = 0;
15010         
15011         for(; i < 42; i++) {
15012             textEls[i].innerHTML = (++extraDays);
15013             d.setDate(d.getDate()+1);
15014             
15015             cells[i].className = "fc-future fc-other-month";
15016             setCellClass(this, cells[i]);
15017         }
15018         
15019         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15020         
15021         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15022         
15023         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15024         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15025         
15026         if(totalRows != 6){
15027             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15028             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15029         }
15030         
15031         this.fireEvent('monthchange', this, date);
15032         
15033         
15034         /*
15035         if(!this.internalRender){
15036             var main = this.el.dom.firstChild;
15037             var w = main.offsetWidth;
15038             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15039             Roo.fly(main).setWidth(w);
15040             this.internalRender = true;
15041             // opera does not respect the auto grow header center column
15042             // then, after it gets a width opera refuses to recalculate
15043             // without a second pass
15044             if(Roo.isOpera && !this.secondPass){
15045                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15046                 this.secondPass = true;
15047                 this.update.defer(10, this, [date]);
15048             }
15049         }
15050         */
15051         
15052     },
15053     
15054     findCell : function(dt) {
15055         dt = dt.clearTime().getTime();
15056         var ret = false;
15057         this.cells.each(function(c){
15058             //Roo.log("check " +c.dateValue + '?=' + dt);
15059             if(c.dateValue == dt){
15060                 ret = c;
15061                 return false;
15062             }
15063             return true;
15064         });
15065         
15066         return ret;
15067     },
15068     
15069     findCells : function(ev) {
15070         var s = ev.start.clone().clearTime().getTime();
15071        // Roo.log(s);
15072         var e= ev.end.clone().clearTime().getTime();
15073        // Roo.log(e);
15074         var ret = [];
15075         this.cells.each(function(c){
15076              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15077             
15078             if(c.dateValue > e){
15079                 return ;
15080             }
15081             if(c.dateValue < s){
15082                 return ;
15083             }
15084             ret.push(c);
15085         });
15086         
15087         return ret;    
15088     },
15089     
15090 //    findBestRow: function(cells)
15091 //    {
15092 //        var ret = 0;
15093 //        
15094 //        for (var i =0 ; i < cells.length;i++) {
15095 //            ret  = Math.max(cells[i].rows || 0,ret);
15096 //        }
15097 //        return ret;
15098 //        
15099 //    },
15100     
15101     
15102     addItem : function(ev)
15103     {
15104         // look for vertical location slot in
15105         var cells = this.findCells(ev);
15106         
15107 //        ev.row = this.findBestRow(cells);
15108         
15109         // work out the location.
15110         
15111         var crow = false;
15112         var rows = [];
15113         for(var i =0; i < cells.length; i++) {
15114             
15115             cells[i].row = cells[0].row;
15116             
15117             if(i == 0){
15118                 cells[i].row = cells[i].row + 1;
15119             }
15120             
15121             if (!crow) {
15122                 crow = {
15123                     start : cells[i],
15124                     end :  cells[i]
15125                 };
15126                 continue;
15127             }
15128             if (crow.start.getY() == cells[i].getY()) {
15129                 // on same row.
15130                 crow.end = cells[i];
15131                 continue;
15132             }
15133             // different row.
15134             rows.push(crow);
15135             crow = {
15136                 start: cells[i],
15137                 end : cells[i]
15138             };
15139             
15140         }
15141         
15142         rows.push(crow);
15143         ev.els = [];
15144         ev.rows = rows;
15145         ev.cells = cells;
15146         
15147         cells[0].events.push(ev);
15148         
15149         this.calevents.push(ev);
15150     },
15151     
15152     clearEvents: function() {
15153         
15154         if(!this.calevents){
15155             return;
15156         }
15157         
15158         Roo.each(this.cells.elements, function(c){
15159             c.row = 0;
15160             c.events = [];
15161             c.more = [];
15162         });
15163         
15164         Roo.each(this.calevents, function(e) {
15165             Roo.each(e.els, function(el) {
15166                 el.un('mouseenter' ,this.onEventEnter, this);
15167                 el.un('mouseleave' ,this.onEventLeave, this);
15168                 el.remove();
15169             },this);
15170         },this);
15171         
15172         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15173             e.remove();
15174         });
15175         
15176     },
15177     
15178     renderEvents: function()
15179     {   
15180         var _this = this;
15181         
15182         this.cells.each(function(c) {
15183             
15184             if(c.row < 5){
15185                 return;
15186             }
15187             
15188             var ev = c.events;
15189             
15190             var r = 4;
15191             if(c.row != c.events.length){
15192                 r = 4 - (4 - (c.row - c.events.length));
15193             }
15194             
15195             c.events = ev.slice(0, r);
15196             c.more = ev.slice(r);
15197             
15198             if(c.more.length && c.more.length == 1){
15199                 c.events.push(c.more.pop());
15200             }
15201             
15202             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15203             
15204         });
15205             
15206         this.cells.each(function(c) {
15207             
15208             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15209             
15210             
15211             for (var e = 0; e < c.events.length; e++){
15212                 var ev = c.events[e];
15213                 var rows = ev.rows;
15214                 
15215                 for(var i = 0; i < rows.length; i++) {
15216                 
15217                     // how many rows should it span..
15218
15219                     var  cfg = {
15220                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15221                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15222
15223                         unselectable : "on",
15224                         cn : [
15225                             {
15226                                 cls: 'fc-event-inner',
15227                                 cn : [
15228     //                                {
15229     //                                  tag:'span',
15230     //                                  cls: 'fc-event-time',
15231     //                                  html : cells.length > 1 ? '' : ev.time
15232     //                                },
15233                                     {
15234                                       tag:'span',
15235                                       cls: 'fc-event-title',
15236                                       html : String.format('{0}', ev.title)
15237                                     }
15238
15239
15240                                 ]
15241                             },
15242                             {
15243                                 cls: 'ui-resizable-handle ui-resizable-e',
15244                                 html : '&nbsp;&nbsp;&nbsp'
15245                             }
15246
15247                         ]
15248                     };
15249
15250                     if (i == 0) {
15251                         cfg.cls += ' fc-event-start';
15252                     }
15253                     if ((i+1) == rows.length) {
15254                         cfg.cls += ' fc-event-end';
15255                     }
15256
15257                     var ctr = _this.el.select('.fc-event-container',true).first();
15258                     var cg = ctr.createChild(cfg);
15259
15260                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15261                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15262
15263                     var r = (c.more.length) ? 1 : 0;
15264                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15265                     cg.setWidth(ebox.right - sbox.x -2);
15266
15267                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15268                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15269                     cg.on('click', _this.onEventClick, _this, ev);
15270
15271                     ev.els.push(cg);
15272                     
15273                 }
15274                 
15275             }
15276             
15277             
15278             if(c.more.length){
15279                 var  cfg = {
15280                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15281                     style : 'position: absolute',
15282                     unselectable : "on",
15283                     cn : [
15284                         {
15285                             cls: 'fc-event-inner',
15286                             cn : [
15287                                 {
15288                                   tag:'span',
15289                                   cls: 'fc-event-title',
15290                                   html : 'More'
15291                                 }
15292
15293
15294                             ]
15295                         },
15296                         {
15297                             cls: 'ui-resizable-handle ui-resizable-e',
15298                             html : '&nbsp;&nbsp;&nbsp'
15299                         }
15300
15301                     ]
15302                 };
15303
15304                 var ctr = _this.el.select('.fc-event-container',true).first();
15305                 var cg = ctr.createChild(cfg);
15306
15307                 var sbox = c.select('.fc-day-content',true).first().getBox();
15308                 var ebox = c.select('.fc-day-content',true).first().getBox();
15309                 //Roo.log(cg);
15310                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15311                 cg.setWidth(ebox.right - sbox.x -2);
15312
15313                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15314                 
15315             }
15316             
15317         });
15318         
15319         
15320         
15321     },
15322     
15323     onEventEnter: function (e, el,event,d) {
15324         this.fireEvent('evententer', this, el, event);
15325     },
15326     
15327     onEventLeave: function (e, el,event,d) {
15328         this.fireEvent('eventleave', this, el, event);
15329     },
15330     
15331     onEventClick: function (e, el,event,d) {
15332         this.fireEvent('eventclick', this, el, event);
15333     },
15334     
15335     onMonthChange: function () {
15336         this.store.load();
15337     },
15338     
15339     onMoreEventClick: function(e, el, more)
15340     {
15341         var _this = this;
15342         
15343         this.calpopover.placement = 'right';
15344         this.calpopover.setTitle('More');
15345         
15346         this.calpopover.setContent('');
15347         
15348         var ctr = this.calpopover.el.select('.popover-content', true).first();
15349         
15350         Roo.each(more, function(m){
15351             var cfg = {
15352                 cls : 'fc-event-hori fc-event-draggable',
15353                 html : m.title
15354             }
15355             var cg = ctr.createChild(cfg);
15356             
15357             cg.on('click', _this.onEventClick, _this, m);
15358         });
15359         
15360         this.calpopover.show(el);
15361         
15362         
15363     },
15364     
15365     onLoad: function () 
15366     {   
15367         this.calevents = [];
15368         var cal = this;
15369         
15370         if(this.store.getCount() > 0){
15371             this.store.data.each(function(d){
15372                cal.addItem({
15373                     id : d.data.id,
15374                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15375                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15376                     time : d.data.start_time,
15377                     title : d.data.title,
15378                     description : d.data.description,
15379                     venue : d.data.venue
15380                 });
15381             });
15382         }
15383         
15384         this.renderEvents();
15385         
15386         if(this.calevents.length && this.loadMask){
15387             this.maskEl.hide();
15388         }
15389     },
15390     
15391     onBeforeLoad: function()
15392     {
15393         this.clearEvents();
15394         if(this.loadMask){
15395             this.maskEl.show();
15396         }
15397     }
15398 });
15399
15400  
15401  /*
15402  * - LGPL
15403  *
15404  * element
15405  * 
15406  */
15407
15408 /**
15409  * @class Roo.bootstrap.Popover
15410  * @extends Roo.bootstrap.Component
15411  * Bootstrap Popover class
15412  * @cfg {String} html contents of the popover   (or false to use children..)
15413  * @cfg {String} title of popover (or false to hide)
15414  * @cfg {String} placement how it is placed
15415  * @cfg {String} trigger click || hover (or false to trigger manually)
15416  * @cfg {String} over what (parent or false to trigger manually.)
15417  * @cfg {Number} delay - delay before showing
15418  
15419  * @constructor
15420  * Create a new Popover
15421  * @param {Object} config The config object
15422  */
15423
15424 Roo.bootstrap.Popover = function(config){
15425     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15426 };
15427
15428 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15429     
15430     title: 'Fill in a title',
15431     html: false,
15432     
15433     placement : 'right',
15434     trigger : 'hover', // hover
15435     
15436     delay : 0,
15437     
15438     over: 'parent',
15439     
15440     can_build_overlaid : false,
15441     
15442     getChildContainer : function()
15443     {
15444         return this.el.select('.popover-content',true).first();
15445     },
15446     
15447     getAutoCreate : function(){
15448          Roo.log('make popover?');
15449         var cfg = {
15450            cls : 'popover roo-dynamic',
15451            style: 'display:block',
15452            cn : [
15453                 {
15454                     cls : 'arrow'
15455                 },
15456                 {
15457                     cls : 'popover-inner',
15458                     cn : [
15459                         {
15460                             tag: 'h3',
15461                             cls: 'popover-title',
15462                             html : this.title
15463                         },
15464                         {
15465                             cls : 'popover-content',
15466                             html : this.html
15467                         }
15468                     ]
15469                     
15470                 }
15471            ]
15472         };
15473         
15474         return cfg;
15475     },
15476     setTitle: function(str)
15477     {
15478         this.title = str;
15479         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15480     },
15481     setContent: function(str)
15482     {
15483         this.html = str;
15484         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15485     },
15486     // as it get's added to the bottom of the page.
15487     onRender : function(ct, position)
15488     {
15489         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15490         if(!this.el){
15491             var cfg = Roo.apply({},  this.getAutoCreate());
15492             cfg.id = Roo.id();
15493             
15494             if (this.cls) {
15495                 cfg.cls += ' ' + this.cls;
15496             }
15497             if (this.style) {
15498                 cfg.style = this.style;
15499             }
15500             Roo.log("adding to ")
15501             this.el = Roo.get(document.body).createChild(cfg, position);
15502             Roo.log(this.el);
15503         }
15504         this.initEvents();
15505     },
15506     
15507     initEvents : function()
15508     {
15509         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15510         this.el.enableDisplayMode('block');
15511         this.el.hide();
15512         if (this.over === false) {
15513             return; 
15514         }
15515         if (this.triggers === false) {
15516             return;
15517         }
15518         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15519         var triggers = this.trigger ? this.trigger.split(' ') : [];
15520         Roo.each(triggers, function(trigger) {
15521         
15522             if (trigger == 'click') {
15523                 on_el.on('click', this.toggle, this);
15524             } else if (trigger != 'manual') {
15525                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15526                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15527       
15528                 on_el.on(eventIn  ,this.enter, this);
15529                 on_el.on(eventOut, this.leave, this);
15530             }
15531         }, this);
15532         
15533     },
15534     
15535     
15536     // private
15537     timeout : null,
15538     hoverState : null,
15539     
15540     toggle : function () {
15541         this.hoverState == 'in' ? this.leave() : this.enter();
15542     },
15543     
15544     enter : function () {
15545        
15546     
15547         clearTimeout(this.timeout);
15548     
15549         this.hoverState = 'in';
15550     
15551         if (!this.delay || !this.delay.show) {
15552             this.show();
15553             return;
15554         }
15555         var _t = this;
15556         this.timeout = setTimeout(function () {
15557             if (_t.hoverState == 'in') {
15558                 _t.show();
15559             }
15560         }, this.delay.show)
15561     },
15562     leave : function() {
15563         clearTimeout(this.timeout);
15564     
15565         this.hoverState = 'out';
15566     
15567         if (!this.delay || !this.delay.hide) {
15568             this.hide();
15569             return;
15570         }
15571         var _t = this;
15572         this.timeout = setTimeout(function () {
15573             if (_t.hoverState == 'out') {
15574                 _t.hide();
15575             }
15576         }, this.delay.hide)
15577     },
15578     
15579     show : function (on_el)
15580     {
15581         if (!on_el) {
15582             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15583         }
15584         // set content.
15585         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15586         if (this.html !== false) {
15587             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15588         }
15589         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15590         if (!this.title.length) {
15591             this.el.select('.popover-title',true).hide();
15592         }
15593         
15594         var placement = typeof this.placement == 'function' ?
15595             this.placement.call(this, this.el, on_el) :
15596             this.placement;
15597             
15598         var autoToken = /\s?auto?\s?/i;
15599         var autoPlace = autoToken.test(placement);
15600         if (autoPlace) {
15601             placement = placement.replace(autoToken, '') || 'top';
15602         }
15603         
15604         //this.el.detach()
15605         //this.el.setXY([0,0]);
15606         this.el.show();
15607         this.el.dom.style.display='block';
15608         this.el.addClass(placement);
15609         
15610         //this.el.appendTo(on_el);
15611         
15612         var p = this.getPosition();
15613         var box = this.el.getBox();
15614         
15615         if (autoPlace) {
15616             // fixme..
15617         }
15618         var align = Roo.bootstrap.Popover.alignment[placement];
15619         this.el.alignTo(on_el, align[0],align[1]);
15620         //var arrow = this.el.select('.arrow',true).first();
15621         //arrow.set(align[2], 
15622         
15623         this.el.addClass('in');
15624         
15625         
15626         if (this.el.hasClass('fade')) {
15627             // fade it?
15628         }
15629         
15630     },
15631     hide : function()
15632     {
15633         this.el.setXY([0,0]);
15634         this.el.removeClass('in');
15635         this.el.hide();
15636         this.hoverState = null;
15637         
15638     }
15639     
15640 });
15641
15642 Roo.bootstrap.Popover.alignment = {
15643     'left' : ['r-l', [-10,0], 'right'],
15644     'right' : ['l-r', [10,0], 'left'],
15645     'bottom' : ['t-b', [0,10], 'top'],
15646     'top' : [ 'b-t', [0,-10], 'bottom']
15647 };
15648
15649  /*
15650  * - LGPL
15651  *
15652  * Progress
15653  * 
15654  */
15655
15656 /**
15657  * @class Roo.bootstrap.Progress
15658  * @extends Roo.bootstrap.Component
15659  * Bootstrap Progress class
15660  * @cfg {Boolean} striped striped of the progress bar
15661  * @cfg {Boolean} active animated of the progress bar
15662  * 
15663  * 
15664  * @constructor
15665  * Create a new Progress
15666  * @param {Object} config The config object
15667  */
15668
15669 Roo.bootstrap.Progress = function(config){
15670     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15671 };
15672
15673 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15674     
15675     striped : false,
15676     active: false,
15677     
15678     getAutoCreate : function(){
15679         var cfg = {
15680             tag: 'div',
15681             cls: 'progress'
15682         };
15683         
15684         
15685         if(this.striped){
15686             cfg.cls += ' progress-striped';
15687         }
15688       
15689         if(this.active){
15690             cfg.cls += ' active';
15691         }
15692         
15693         
15694         return cfg;
15695     }
15696    
15697 });
15698
15699  
15700
15701  /*
15702  * - LGPL
15703  *
15704  * ProgressBar
15705  * 
15706  */
15707
15708 /**
15709  * @class Roo.bootstrap.ProgressBar
15710  * @extends Roo.bootstrap.Component
15711  * Bootstrap ProgressBar class
15712  * @cfg {Number} aria_valuenow aria-value now
15713  * @cfg {Number} aria_valuemin aria-value min
15714  * @cfg {Number} aria_valuemax aria-value max
15715  * @cfg {String} label label for the progress bar
15716  * @cfg {String} panel (success | info | warning | danger )
15717  * @cfg {String} role role of the progress bar
15718  * @cfg {String} sr_only text
15719  * 
15720  * 
15721  * @constructor
15722  * Create a new ProgressBar
15723  * @param {Object} config The config object
15724  */
15725
15726 Roo.bootstrap.ProgressBar = function(config){
15727     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15728 };
15729
15730 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15731     
15732     aria_valuenow : 0,
15733     aria_valuemin : 0,
15734     aria_valuemax : 100,
15735     label : false,
15736     panel : false,
15737     role : false,
15738     sr_only: false,
15739     
15740     getAutoCreate : function()
15741     {
15742         
15743         var cfg = {
15744             tag: 'div',
15745             cls: 'progress-bar',
15746             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15747         };
15748         
15749         if(this.sr_only){
15750             cfg.cn = {
15751                 tag: 'span',
15752                 cls: 'sr-only',
15753                 html: this.sr_only
15754             }
15755         }
15756         
15757         if(this.role){
15758             cfg.role = this.role;
15759         }
15760         
15761         if(this.aria_valuenow){
15762             cfg['aria-valuenow'] = this.aria_valuenow;
15763         }
15764         
15765         if(this.aria_valuemin){
15766             cfg['aria-valuemin'] = this.aria_valuemin;
15767         }
15768         
15769         if(this.aria_valuemax){
15770             cfg['aria-valuemax'] = this.aria_valuemax;
15771         }
15772         
15773         if(this.label && !this.sr_only){
15774             cfg.html = this.label;
15775         }
15776         
15777         if(this.panel){
15778             cfg.cls += ' progress-bar-' + this.panel;
15779         }
15780         
15781         return cfg;
15782     },
15783     
15784     update : function(aria_valuenow)
15785     {
15786         this.aria_valuenow = aria_valuenow;
15787         
15788         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15789     }
15790    
15791 });
15792
15793  
15794
15795  /*
15796  * - LGPL
15797  *
15798  * column
15799  * 
15800  */
15801
15802 /**
15803  * @class Roo.bootstrap.TabGroup
15804  * @extends Roo.bootstrap.Column
15805  * Bootstrap Column class
15806  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15807  * @cfg {Boolean} carousel true to make the group behave like a carousel
15808  * @cfg {Number} bullets show the panel pointer.. default 0
15809  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15810  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15811  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15812  * 
15813  * @constructor
15814  * Create a new TabGroup
15815  * @param {Object} config The config object
15816  */
15817
15818 Roo.bootstrap.TabGroup = function(config){
15819     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15820     if (!this.navId) {
15821         this.navId = Roo.id();
15822     }
15823     this.tabs = [];
15824     Roo.bootstrap.TabGroup.register(this);
15825     
15826 };
15827
15828 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15829     
15830     carousel : false,
15831     transition : false,
15832     bullets : 0,
15833     timer : 0,
15834     autoslide : false,
15835     slideFn : false,
15836     slideOnTouch : false,
15837     
15838     getAutoCreate : function()
15839     {
15840         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15841         
15842         cfg.cls += ' tab-content';
15843         
15844         Roo.log('get auto create...............');
15845         
15846         if (this.carousel) {
15847             cfg.cls += ' carousel slide';
15848             
15849             cfg.cn = [{
15850                cls : 'carousel-inner'
15851             }];
15852         
15853             if(this.bullets > 0 && !Roo.isTouch){
15854                 
15855                 var bullets = {
15856                     cls : 'carousel-bullets',
15857                     cn : []
15858                 };
15859                 
15860                 if(this.bullets_cls){
15861                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15862                 }
15863                 
15864                 for (var i = 0; i < this.bullets; i++){
15865                     bullets.cn.push({
15866                         cls : 'bullet bullet-' + i
15867                     });
15868                 }
15869                 
15870                 bullets.cn.push({
15871                     cls : 'clear'
15872                 });
15873                 
15874                 cfg.cn[0].cn = bullets;
15875             }
15876         }
15877         
15878         return cfg;
15879     },
15880     
15881     initEvents:  function()
15882     {
15883         Roo.log('-------- init events on tab group ---------');
15884         
15885         if(this.bullets > 0 && !Roo.isTouch){
15886             this.initBullet();
15887         }
15888         
15889         Roo.log(this);
15890         
15891         if(Roo.isTouch && this.slideOnTouch){
15892             this.el.on("touchstart", this.onTouchStart, this);
15893         }
15894         
15895         if(this.autoslide){
15896             var _this = this;
15897             
15898             this.slideFn = window.setInterval(function() {
15899                 _this.showPanelNext();
15900             }, this.timer);
15901         }
15902         
15903     },
15904     
15905     onTouchStart : function(e, el, o)
15906     {
15907         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15908             return;
15909         }
15910         
15911         this.showPanelNext();
15912     },
15913     
15914     getChildContainer : function()
15915     {
15916         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15917     },
15918     
15919     /**
15920     * register a Navigation item
15921     * @param {Roo.bootstrap.NavItem} the navitem to add
15922     */
15923     register : function(item)
15924     {
15925         this.tabs.push( item);
15926         item.navId = this.navId; // not really needed..
15927     
15928     },
15929     
15930     getActivePanel : function()
15931     {
15932         var r = false;
15933         Roo.each(this.tabs, function(t) {
15934             if (t.active) {
15935                 r = t;
15936                 return false;
15937             }
15938             return null;
15939         });
15940         return r;
15941         
15942     },
15943     getPanelByName : function(n)
15944     {
15945         var r = false;
15946         Roo.each(this.tabs, function(t) {
15947             if (t.tabId == n) {
15948                 r = t;
15949                 return false;
15950             }
15951             return null;
15952         });
15953         return r;
15954     },
15955     indexOfPanel : function(p)
15956     {
15957         var r = false;
15958         Roo.each(this.tabs, function(t,i) {
15959             if (t.tabId == p.tabId) {
15960                 r = i;
15961                 return false;
15962             }
15963             return null;
15964         });
15965         return r;
15966     },
15967     /**
15968      * show a specific panel
15969      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15970      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15971      */
15972     showPanel : function (pan)
15973     {
15974         if(this.transition){
15975             Roo.log("waiting for the transitionend");
15976             return;
15977         }
15978         
15979         if (typeof(pan) == 'number') {
15980             pan = this.tabs[pan];
15981         }
15982         if (typeof(pan) == 'string') {
15983             pan = this.getPanelByName(pan);
15984         }
15985         if (pan.tabId == this.getActivePanel().tabId) {
15986             return true;
15987         }
15988         var cur = this.getActivePanel();
15989         
15990         if (false === cur.fireEvent('beforedeactivate')) {
15991             return false;
15992         }
15993         
15994         if(this.bullets > 0 && !Roo.isTouch){
15995             this.setActiveBullet(this.indexOfPanel(pan));
15996         }
15997         
15998         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15999             
16000             this.transition = true;
16001             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16002             var lr = dir == 'next' ? 'left' : 'right';
16003             pan.el.addClass(dir); // or prev
16004             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16005             cur.el.addClass(lr); // or right
16006             pan.el.addClass(lr);
16007             
16008             var _this = this;
16009             cur.el.on('transitionend', function() {
16010                 Roo.log("trans end?");
16011                 
16012                 pan.el.removeClass([lr,dir]);
16013                 pan.setActive(true);
16014                 
16015                 cur.el.removeClass([lr]);
16016                 cur.setActive(false);
16017                 
16018                 _this.transition = false;
16019                 
16020             }, this, { single:  true } );
16021             
16022             return true;
16023         }
16024         
16025         cur.setActive(false);
16026         pan.setActive(true);
16027         
16028         return true;
16029         
16030     },
16031     showPanelNext : function()
16032     {
16033         var i = this.indexOfPanel(this.getActivePanel());
16034         
16035         if (i >= this.tabs.length - 1 && !this.autoslide) {
16036             return;
16037         }
16038         
16039         if (i >= this.tabs.length - 1 && this.autoslide) {
16040             i = -1;
16041         }
16042         
16043         this.showPanel(this.tabs[i+1]);
16044     },
16045     
16046     showPanelPrev : function()
16047     {
16048         var i = this.indexOfPanel(this.getActivePanel());
16049         
16050         if (i  < 1 && !this.autoslide) {
16051             return;
16052         }
16053         
16054         if (i < 1 && this.autoslide) {
16055             i = this.tabs.length;
16056         }
16057         
16058         this.showPanel(this.tabs[i-1]);
16059     },
16060     
16061     initBullet : function()
16062     {
16063         if(Roo.isTouch){
16064             return;
16065         }
16066         
16067         var _this = this;
16068         
16069         for (var i = 0; i < this.bullets; i++){
16070             var bullet = this.el.select('.bullet-' + i, true).first();
16071
16072             if(!bullet){
16073                 continue;
16074             }
16075
16076             bullet.on('click', (function(e, el, o, ii, t){
16077
16078                 e.preventDefault();
16079
16080                 _this.showPanel(ii);
16081
16082                 if(_this.autoslide && _this.slideFn){
16083                     clearInterval(_this.slideFn);
16084                     _this.slideFn = window.setInterval(function() {
16085                         _this.showPanelNext();
16086                     }, _this.timer);
16087                 }
16088
16089             }).createDelegate(this, [i, bullet], true));
16090         }
16091     },
16092     
16093     setActiveBullet : function(i)
16094     {
16095         if(Roo.isTouch){
16096             return;
16097         }
16098         
16099         Roo.each(this.el.select('.bullet', true).elements, function(el){
16100             el.removeClass('selected');
16101         });
16102
16103         var bullet = this.el.select('.bullet-' + i, true).first();
16104         
16105         if(!bullet){
16106             return;
16107         }
16108         
16109         bullet.addClass('selected');
16110     }
16111     
16112     
16113   
16114 });
16115
16116  
16117
16118  
16119  
16120 Roo.apply(Roo.bootstrap.TabGroup, {
16121     
16122     groups: {},
16123      /**
16124     * register a Navigation Group
16125     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16126     */
16127     register : function(navgrp)
16128     {
16129         this.groups[navgrp.navId] = navgrp;
16130         
16131     },
16132     /**
16133     * fetch a Navigation Group based on the navigation ID
16134     * if one does not exist , it will get created.
16135     * @param {string} the navgroup to add
16136     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16137     */
16138     get: function(navId) {
16139         if (typeof(this.groups[navId]) == 'undefined') {
16140             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16141         }
16142         return this.groups[navId] ;
16143     }
16144     
16145     
16146     
16147 });
16148
16149  /*
16150  * - LGPL
16151  *
16152  * TabPanel
16153  * 
16154  */
16155
16156 /**
16157  * @class Roo.bootstrap.TabPanel
16158  * @extends Roo.bootstrap.Component
16159  * Bootstrap TabPanel class
16160  * @cfg {Boolean} active panel active
16161  * @cfg {String} html panel content
16162  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16163  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16164  * 
16165  * 
16166  * @constructor
16167  * Create a new TabPanel
16168  * @param {Object} config The config object
16169  */
16170
16171 Roo.bootstrap.TabPanel = function(config){
16172     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16173     this.addEvents({
16174         /**
16175              * @event changed
16176              * Fires when the active status changes
16177              * @param {Roo.bootstrap.TabPanel} this
16178              * @param {Boolean} state the new state
16179             
16180          */
16181         'changed': true,
16182         /**
16183              * @event beforedeactivate
16184              * Fires before a tab is de-activated - can be used to do validation on a form.
16185              * @param {Roo.bootstrap.TabPanel} this
16186              * @return {Boolean} false if there is an error
16187             
16188          */
16189         'beforedeactivate': true
16190      });
16191     
16192     this.tabId = this.tabId || Roo.id();
16193   
16194 };
16195
16196 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16197     
16198     active: false,
16199     html: false,
16200     tabId: false,
16201     navId : false,
16202     
16203     getAutoCreate : function(){
16204         var cfg = {
16205             tag: 'div',
16206             // item is needed for carousel - not sure if it has any effect otherwise
16207             cls: 'tab-pane item',
16208             html: this.html || ''
16209         };
16210         
16211         if(this.active){
16212             cfg.cls += ' active';
16213         }
16214         
16215         if(this.tabId){
16216             cfg.tabId = this.tabId;
16217         }
16218         
16219         
16220         return cfg;
16221     },
16222     
16223     initEvents:  function()
16224     {
16225         Roo.log('-------- init events on tab panel ---------');
16226         
16227         var p = this.parent();
16228         this.navId = this.navId || p.navId;
16229         
16230         if (typeof(this.navId) != 'undefined') {
16231             // not really needed.. but just in case.. parent should be a NavGroup.
16232             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16233             Roo.log(['register', tg, this]);
16234             tg.register(this);
16235             
16236             var i = tg.tabs.length - 1;
16237             
16238             if(this.active && tg.bullets > 0 && i < tg.bullets){
16239                 tg.setActiveBullet(i);
16240             }
16241         }
16242         
16243     },
16244     
16245     
16246     onRender : function(ct, position)
16247     {
16248        // Roo.log("Call onRender: " + this.xtype);
16249         
16250         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16251         
16252         
16253         
16254         
16255         
16256     },
16257     
16258     setActive: function(state)
16259     {
16260         Roo.log("panel - set active " + this.tabId + "=" + state);
16261         
16262         this.active = state;
16263         if (!state) {
16264             this.el.removeClass('active');
16265             
16266         } else  if (!this.el.hasClass('active')) {
16267             this.el.addClass('active');
16268         }
16269         
16270         this.fireEvent('changed', this, state);
16271     }
16272     
16273     
16274 });
16275  
16276
16277  
16278
16279  /*
16280  * - LGPL
16281  *
16282  * DateField
16283  * 
16284  */
16285
16286 /**
16287  * @class Roo.bootstrap.DateField
16288  * @extends Roo.bootstrap.Input
16289  * Bootstrap DateField class
16290  * @cfg {Number} weekStart default 0
16291  * @cfg {String} viewMode default empty, (months|years)
16292  * @cfg {String} minViewMode default empty, (months|years)
16293  * @cfg {Number} startDate default -Infinity
16294  * @cfg {Number} endDate default Infinity
16295  * @cfg {Boolean} todayHighlight default false
16296  * @cfg {Boolean} todayBtn default false
16297  * @cfg {Boolean} calendarWeeks default false
16298  * @cfg {Object} daysOfWeekDisabled default empty
16299  * @cfg {Boolean} singleMode default false (true | false)
16300  * 
16301  * @cfg {Boolean} keyboardNavigation default true
16302  * @cfg {String} language default en
16303  * 
16304  * @constructor
16305  * Create a new DateField
16306  * @param {Object} config The config object
16307  */
16308
16309 Roo.bootstrap.DateField = function(config){
16310     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16311      this.addEvents({
16312             /**
16313              * @event show
16314              * Fires when this field show.
16315              * @param {Roo.bootstrap.DateField} this
16316              * @param {Mixed} date The date value
16317              */
16318             show : true,
16319             /**
16320              * @event show
16321              * Fires when this field hide.
16322              * @param {Roo.bootstrap.DateField} this
16323              * @param {Mixed} date The date value
16324              */
16325             hide : true,
16326             /**
16327              * @event select
16328              * Fires when select a date.
16329              * @param {Roo.bootstrap.DateField} this
16330              * @param {Mixed} date The date value
16331              */
16332             select : true
16333         });
16334 };
16335
16336 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16337     
16338     /**
16339      * @cfg {String} format
16340      * The default date format string which can be overriden for localization support.  The format must be
16341      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16342      */
16343     format : "m/d/y",
16344     /**
16345      * @cfg {String} altFormats
16346      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16347      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16348      */
16349     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16350     
16351     weekStart : 0,
16352     
16353     viewMode : '',
16354     
16355     minViewMode : '',
16356     
16357     todayHighlight : false,
16358     
16359     todayBtn: false,
16360     
16361     language: 'en',
16362     
16363     keyboardNavigation: true,
16364     
16365     calendarWeeks: false,
16366     
16367     startDate: -Infinity,
16368     
16369     endDate: Infinity,
16370     
16371     daysOfWeekDisabled: [],
16372     
16373     _events: [],
16374     
16375     singleMode : false,
16376     
16377     UTCDate: function()
16378     {
16379         return new Date(Date.UTC.apply(Date, arguments));
16380     },
16381     
16382     UTCToday: function()
16383     {
16384         var today = new Date();
16385         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16386     },
16387     
16388     getDate: function() {
16389             var d = this.getUTCDate();
16390             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16391     },
16392     
16393     getUTCDate: function() {
16394             return this.date;
16395     },
16396     
16397     setDate: function(d) {
16398             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16399     },
16400     
16401     setUTCDate: function(d) {
16402             this.date = d;
16403             this.setValue(this.formatDate(this.date));
16404     },
16405         
16406     onRender: function(ct, position)
16407     {
16408         
16409         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16410         
16411         this.language = this.language || 'en';
16412         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16413         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16414         
16415         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16416         this.format = this.format || 'm/d/y';
16417         this.isInline = false;
16418         this.isInput = true;
16419         this.component = this.el.select('.add-on', true).first() || false;
16420         this.component = (this.component && this.component.length === 0) ? false : this.component;
16421         this.hasInput = this.component && this.inputEL().length;
16422         
16423         if (typeof(this.minViewMode === 'string')) {
16424             switch (this.minViewMode) {
16425                 case 'months':
16426                     this.minViewMode = 1;
16427                     break;
16428                 case 'years':
16429                     this.minViewMode = 2;
16430                     break;
16431                 default:
16432                     this.minViewMode = 0;
16433                     break;
16434             }
16435         }
16436         
16437         if (typeof(this.viewMode === 'string')) {
16438             switch (this.viewMode) {
16439                 case 'months':
16440                     this.viewMode = 1;
16441                     break;
16442                 case 'years':
16443                     this.viewMode = 2;
16444                     break;
16445                 default:
16446                     this.viewMode = 0;
16447                     break;
16448             }
16449         }
16450                 
16451         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16452         
16453 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16454         
16455         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16456         
16457         this.picker().on('mousedown', this.onMousedown, this);
16458         this.picker().on('click', this.onClick, this);
16459         
16460         this.picker().addClass('datepicker-dropdown');
16461         
16462         this.startViewMode = this.viewMode;
16463         
16464         if(this.singleMode){
16465             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16466                 v.setVisibilityMode(Roo.Element.DISPLAY)
16467                 v.hide();
16468             });
16469             
16470             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16471                 v.setStyle('width', '189px');
16472             });
16473         }
16474         
16475         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16476             if(!this.calendarWeeks){
16477                 v.remove();
16478                 return;
16479             }
16480             
16481             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16482             v.attr('colspan', function(i, val){
16483                 return parseInt(val) + 1;
16484             });
16485         })
16486                         
16487         
16488         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16489         
16490         this.setStartDate(this.startDate);
16491         this.setEndDate(this.endDate);
16492         
16493         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16494         
16495         this.fillDow();
16496         this.fillMonths();
16497         this.update();
16498         this.showMode();
16499         
16500         if(this.isInline) {
16501             this.show();
16502         }
16503     },
16504     
16505     picker : function()
16506     {
16507         return this.pickerEl;
16508 //        return this.el.select('.datepicker', true).first();
16509     },
16510     
16511     fillDow: function()
16512     {
16513         var dowCnt = this.weekStart;
16514         
16515         var dow = {
16516             tag: 'tr',
16517             cn: [
16518                 
16519             ]
16520         };
16521         
16522         if(this.calendarWeeks){
16523             dow.cn.push({
16524                 tag: 'th',
16525                 cls: 'cw',
16526                 html: '&nbsp;'
16527             })
16528         }
16529         
16530         while (dowCnt < this.weekStart + 7) {
16531             dow.cn.push({
16532                 tag: 'th',
16533                 cls: 'dow',
16534                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16535             });
16536         }
16537         
16538         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16539     },
16540     
16541     fillMonths: function()
16542     {    
16543         var i = 0;
16544         var months = this.picker().select('>.datepicker-months td', true).first();
16545         
16546         months.dom.innerHTML = '';
16547         
16548         while (i < 12) {
16549             var month = {
16550                 tag: 'span',
16551                 cls: 'month',
16552                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16553             }
16554             
16555             months.createChild(month);
16556         }
16557         
16558     },
16559     
16560     update: function()
16561     {
16562         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;
16563         
16564         if (this.date < this.startDate) {
16565             this.viewDate = new Date(this.startDate);
16566         } else if (this.date > this.endDate) {
16567             this.viewDate = new Date(this.endDate);
16568         } else {
16569             this.viewDate = new Date(this.date);
16570         }
16571         
16572         this.fill();
16573     },
16574     
16575     fill: function() 
16576     {
16577         var d = new Date(this.viewDate),
16578                 year = d.getUTCFullYear(),
16579                 month = d.getUTCMonth(),
16580                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16581                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16582                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16583                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16584                 currentDate = this.date && this.date.valueOf(),
16585                 today = this.UTCToday();
16586         
16587         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16588         
16589 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16590         
16591 //        this.picker.select('>tfoot th.today').
16592 //                                              .text(dates[this.language].today)
16593 //                                              .toggle(this.todayBtn !== false);
16594     
16595         this.updateNavArrows();
16596         this.fillMonths();
16597                                                 
16598         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16599         
16600         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16601          
16602         prevMonth.setUTCDate(day);
16603         
16604         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16605         
16606         var nextMonth = new Date(prevMonth);
16607         
16608         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16609         
16610         nextMonth = nextMonth.valueOf();
16611         
16612         var fillMonths = false;
16613         
16614         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16615         
16616         while(prevMonth.valueOf() < nextMonth) {
16617             var clsName = '';
16618             
16619             if (prevMonth.getUTCDay() === this.weekStart) {
16620                 if(fillMonths){
16621                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16622                 }
16623                     
16624                 fillMonths = {
16625                     tag: 'tr',
16626                     cn: []
16627                 };
16628                 
16629                 if(this.calendarWeeks){
16630                     // ISO 8601: First week contains first thursday.
16631                     // ISO also states week starts on Monday, but we can be more abstract here.
16632                     var
16633                     // Start of current week: based on weekstart/current date
16634                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16635                     // Thursday of this week
16636                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16637                     // First Thursday of year, year from thursday
16638                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16639                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16640                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16641                     
16642                     fillMonths.cn.push({
16643                         tag: 'td',
16644                         cls: 'cw',
16645                         html: calWeek
16646                     });
16647                 }
16648             }
16649             
16650             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16651                 clsName += ' old';
16652             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16653                 clsName += ' new';
16654             }
16655             if (this.todayHighlight &&
16656                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16657                 prevMonth.getUTCMonth() == today.getMonth() &&
16658                 prevMonth.getUTCDate() == today.getDate()) {
16659                 clsName += ' today';
16660             }
16661             
16662             if (currentDate && prevMonth.valueOf() === currentDate) {
16663                 clsName += ' active';
16664             }
16665             
16666             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16667                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16668                     clsName += ' disabled';
16669             }
16670             
16671             fillMonths.cn.push({
16672                 tag: 'td',
16673                 cls: 'day ' + clsName,
16674                 html: prevMonth.getDate()
16675             })
16676             
16677             prevMonth.setDate(prevMonth.getDate()+1);
16678         }
16679           
16680         var currentYear = this.date && this.date.getUTCFullYear();
16681         var currentMonth = this.date && this.date.getUTCMonth();
16682         
16683         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16684         
16685         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16686             v.removeClass('active');
16687             
16688             if(currentYear === year && k === currentMonth){
16689                 v.addClass('active');
16690             }
16691             
16692             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16693                 v.addClass('disabled');
16694             }
16695             
16696         });
16697         
16698         
16699         year = parseInt(year/10, 10) * 10;
16700         
16701         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16702         
16703         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16704         
16705         year -= 1;
16706         for (var i = -1; i < 11; i++) {
16707             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16708                 tag: 'span',
16709                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16710                 html: year
16711             })
16712             
16713             year += 1;
16714         }
16715     },
16716     
16717     showMode: function(dir) 
16718     {
16719         if (dir) {
16720             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16721         }
16722         
16723         Roo.each(this.picker().select('>div',true).elements, function(v){
16724             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16725             v.hide();
16726         });
16727         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16728     },
16729     
16730     place: function()
16731     {
16732         if(this.isInline) return;
16733         
16734         this.picker().removeClass(['bottom', 'top']);
16735         
16736         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16737             /*
16738              * place to the top of element!
16739              *
16740              */
16741             
16742             this.picker().addClass('top');
16743             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16744             
16745             return;
16746         }
16747         
16748         this.picker().addClass('bottom');
16749         
16750         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16751     },
16752     
16753     parseDate : function(value)
16754     {
16755         if(!value || value instanceof Date){
16756             return value;
16757         }
16758         var v = Date.parseDate(value, this.format);
16759         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16760             v = Date.parseDate(value, 'Y-m-d');
16761         }
16762         if(!v && this.altFormats){
16763             if(!this.altFormatsArray){
16764                 this.altFormatsArray = this.altFormats.split("|");
16765             }
16766             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16767                 v = Date.parseDate(value, this.altFormatsArray[i]);
16768             }
16769         }
16770         return v;
16771     },
16772     
16773     formatDate : function(date, fmt)
16774     {   
16775         return (!date || !(date instanceof Date)) ?
16776         date : date.dateFormat(fmt || this.format);
16777     },
16778     
16779     onFocus : function()
16780     {
16781         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16782         this.show();
16783     },
16784     
16785     onBlur : function()
16786     {
16787         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16788         
16789         var d = this.inputEl().getValue();
16790         
16791         this.setValue(d);
16792                 
16793         this.hide();
16794     },
16795     
16796     show : function()
16797     {
16798         this.picker().show();
16799         this.update();
16800         this.place();
16801         
16802         this.fireEvent('show', this, this.date);
16803     },
16804     
16805     hide : function()
16806     {
16807         if(this.isInline) return;
16808         this.picker().hide();
16809         this.viewMode = this.startViewMode;
16810         this.showMode();
16811         
16812         this.fireEvent('hide', this, this.date);
16813         
16814     },
16815     
16816     onMousedown: function(e)
16817     {
16818         e.stopPropagation();
16819         e.preventDefault();
16820     },
16821     
16822     keyup: function(e)
16823     {
16824         Roo.bootstrap.DateField.superclass.keyup.call(this);
16825         this.update();
16826     },
16827
16828     setValue: function(v)
16829     {
16830         
16831         // v can be a string or a date..
16832         
16833         
16834         var d = new Date(this.parseDate(v) ).clearTime();
16835         
16836         if(isNaN(d.getTime())){
16837             this.date = this.viewDate = '';
16838             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16839             return;
16840         }
16841         
16842         v = this.formatDate(d);
16843         
16844         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16845         
16846         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16847      
16848         this.update();
16849
16850         this.fireEvent('select', this, this.date);
16851         
16852     },
16853     
16854     getValue: function()
16855     {
16856         return this.formatDate(this.date);
16857     },
16858     
16859     fireKey: function(e)
16860     {
16861         if (!this.picker().isVisible()){
16862             if (e.keyCode == 27) // allow escape to hide and re-show picker
16863                 this.show();
16864             return;
16865         }
16866         
16867         var dateChanged = false,
16868         dir, day, month,
16869         newDate, newViewDate;
16870         
16871         switch(e.keyCode){
16872             case 27: // escape
16873                 this.hide();
16874                 e.preventDefault();
16875                 break;
16876             case 37: // left
16877             case 39: // right
16878                 if (!this.keyboardNavigation) break;
16879                 dir = e.keyCode == 37 ? -1 : 1;
16880                 
16881                 if (e.ctrlKey){
16882                     newDate = this.moveYear(this.date, dir);
16883                     newViewDate = this.moveYear(this.viewDate, dir);
16884                 } else if (e.shiftKey){
16885                     newDate = this.moveMonth(this.date, dir);
16886                     newViewDate = this.moveMonth(this.viewDate, dir);
16887                 } else {
16888                     newDate = new Date(this.date);
16889                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16890                     newViewDate = new Date(this.viewDate);
16891                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16892                 }
16893                 if (this.dateWithinRange(newDate)){
16894                     this.date = newDate;
16895                     this.viewDate = newViewDate;
16896                     this.setValue(this.formatDate(this.date));
16897 //                    this.update();
16898                     e.preventDefault();
16899                     dateChanged = true;
16900                 }
16901                 break;
16902             case 38: // up
16903             case 40: // down
16904                 if (!this.keyboardNavigation) break;
16905                 dir = e.keyCode == 38 ? -1 : 1;
16906                 if (e.ctrlKey){
16907                     newDate = this.moveYear(this.date, dir);
16908                     newViewDate = this.moveYear(this.viewDate, dir);
16909                 } else if (e.shiftKey){
16910                     newDate = this.moveMonth(this.date, dir);
16911                     newViewDate = this.moveMonth(this.viewDate, dir);
16912                 } else {
16913                     newDate = new Date(this.date);
16914                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16915                     newViewDate = new Date(this.viewDate);
16916                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16917                 }
16918                 if (this.dateWithinRange(newDate)){
16919                     this.date = newDate;
16920                     this.viewDate = newViewDate;
16921                     this.setValue(this.formatDate(this.date));
16922 //                    this.update();
16923                     e.preventDefault();
16924                     dateChanged = true;
16925                 }
16926                 break;
16927             case 13: // enter
16928                 this.setValue(this.formatDate(this.date));
16929                 this.hide();
16930                 e.preventDefault();
16931                 break;
16932             case 9: // tab
16933                 this.setValue(this.formatDate(this.date));
16934                 this.hide();
16935                 break;
16936             case 16: // shift
16937             case 17: // ctrl
16938             case 18: // alt
16939                 break;
16940             default :
16941                 this.hide();
16942                 
16943         }
16944     },
16945     
16946     
16947     onClick: function(e) 
16948     {
16949         e.stopPropagation();
16950         e.preventDefault();
16951         
16952         var target = e.getTarget();
16953         
16954         if(target.nodeName.toLowerCase() === 'i'){
16955             target = Roo.get(target).dom.parentNode;
16956         }
16957         
16958         var nodeName = target.nodeName;
16959         var className = target.className;
16960         var html = target.innerHTML;
16961         //Roo.log(nodeName);
16962         
16963         switch(nodeName.toLowerCase()) {
16964             case 'th':
16965                 switch(className) {
16966                     case 'switch':
16967                         this.showMode(1);
16968                         break;
16969                     case 'prev':
16970                     case 'next':
16971                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16972                         switch(this.viewMode){
16973                                 case 0:
16974                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16975                                         break;
16976                                 case 1:
16977                                 case 2:
16978                                         this.viewDate = this.moveYear(this.viewDate, dir);
16979                                         break;
16980                         }
16981                         this.fill();
16982                         break;
16983                     case 'today':
16984                         var date = new Date();
16985                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16986 //                        this.fill()
16987                         this.setValue(this.formatDate(this.date));
16988                         
16989                         this.hide();
16990                         break;
16991                 }
16992                 break;
16993             case 'span':
16994                 if (className.indexOf('disabled') < 0) {
16995                     this.viewDate.setUTCDate(1);
16996                     if (className.indexOf('month') > -1) {
16997                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16998                     } else {
16999                         var year = parseInt(html, 10) || 0;
17000                         this.viewDate.setUTCFullYear(year);
17001                         
17002                     }
17003                     
17004                     if(this.singleMode){
17005                         this.setValue(this.formatDate(this.viewDate));
17006                         this.hide();
17007                         return;
17008                     }
17009                     
17010                     this.showMode(-1);
17011                     this.fill();
17012                 }
17013                 break;
17014                 
17015             case 'td':
17016                 //Roo.log(className);
17017                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17018                     var day = parseInt(html, 10) || 1;
17019                     var year = this.viewDate.getUTCFullYear(),
17020                         month = this.viewDate.getUTCMonth();
17021
17022                     if (className.indexOf('old') > -1) {
17023                         if(month === 0 ){
17024                             month = 11;
17025                             year -= 1;
17026                         }else{
17027                             month -= 1;
17028                         }
17029                     } else if (className.indexOf('new') > -1) {
17030                         if (month == 11) {
17031                             month = 0;
17032                             year += 1;
17033                         } else {
17034                             month += 1;
17035                         }
17036                     }
17037                     //Roo.log([year,month,day]);
17038                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17039                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17040 //                    this.fill();
17041                     //Roo.log(this.formatDate(this.date));
17042                     this.setValue(this.formatDate(this.date));
17043                     this.hide();
17044                 }
17045                 break;
17046         }
17047     },
17048     
17049     setStartDate: function(startDate)
17050     {
17051         this.startDate = startDate || -Infinity;
17052         if (this.startDate !== -Infinity) {
17053             this.startDate = this.parseDate(this.startDate);
17054         }
17055         this.update();
17056         this.updateNavArrows();
17057     },
17058
17059     setEndDate: function(endDate)
17060     {
17061         this.endDate = endDate || Infinity;
17062         if (this.endDate !== Infinity) {
17063             this.endDate = this.parseDate(this.endDate);
17064         }
17065         this.update();
17066         this.updateNavArrows();
17067     },
17068     
17069     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17070     {
17071         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17072         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17073             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17074         }
17075         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17076             return parseInt(d, 10);
17077         });
17078         this.update();
17079         this.updateNavArrows();
17080     },
17081     
17082     updateNavArrows: function() 
17083     {
17084         if(this.singleMode){
17085             return;
17086         }
17087         
17088         var d = new Date(this.viewDate),
17089         year = d.getUTCFullYear(),
17090         month = d.getUTCMonth();
17091         
17092         Roo.each(this.picker().select('.prev', true).elements, function(v){
17093             v.show();
17094             switch (this.viewMode) {
17095                 case 0:
17096
17097                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17098                         v.hide();
17099                     }
17100                     break;
17101                 case 1:
17102                 case 2:
17103                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17104                         v.hide();
17105                     }
17106                     break;
17107             }
17108         });
17109         
17110         Roo.each(this.picker().select('.next', true).elements, function(v){
17111             v.show();
17112             switch (this.viewMode) {
17113                 case 0:
17114
17115                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17116                         v.hide();
17117                     }
17118                     break;
17119                 case 1:
17120                 case 2:
17121                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17122                         v.hide();
17123                     }
17124                     break;
17125             }
17126         })
17127     },
17128     
17129     moveMonth: function(date, dir)
17130     {
17131         if (!dir) return date;
17132         var new_date = new Date(date.valueOf()),
17133         day = new_date.getUTCDate(),
17134         month = new_date.getUTCMonth(),
17135         mag = Math.abs(dir),
17136         new_month, test;
17137         dir = dir > 0 ? 1 : -1;
17138         if (mag == 1){
17139             test = dir == -1
17140             // If going back one month, make sure month is not current month
17141             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17142             ? function(){
17143                 return new_date.getUTCMonth() == month;
17144             }
17145             // If going forward one month, make sure month is as expected
17146             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17147             : function(){
17148                 return new_date.getUTCMonth() != new_month;
17149             };
17150             new_month = month + dir;
17151             new_date.setUTCMonth(new_month);
17152             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17153             if (new_month < 0 || new_month > 11)
17154                 new_month = (new_month + 12) % 12;
17155         } else {
17156             // For magnitudes >1, move one month at a time...
17157             for (var i=0; i<mag; i++)
17158                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17159                 new_date = this.moveMonth(new_date, dir);
17160             // ...then reset the day, keeping it in the new month
17161             new_month = new_date.getUTCMonth();
17162             new_date.setUTCDate(day);
17163             test = function(){
17164                 return new_month != new_date.getUTCMonth();
17165             };
17166         }
17167         // Common date-resetting loop -- if date is beyond end of month, make it
17168         // end of month
17169         while (test()){
17170             new_date.setUTCDate(--day);
17171             new_date.setUTCMonth(new_month);
17172         }
17173         return new_date;
17174     },
17175
17176     moveYear: function(date, dir)
17177     {
17178         return this.moveMonth(date, dir*12);
17179     },
17180
17181     dateWithinRange: function(date)
17182     {
17183         return date >= this.startDate && date <= this.endDate;
17184     },
17185
17186     
17187     remove: function() 
17188     {
17189         this.picker().remove();
17190     }
17191    
17192 });
17193
17194 Roo.apply(Roo.bootstrap.DateField,  {
17195     
17196     head : {
17197         tag: 'thead',
17198         cn: [
17199         {
17200             tag: 'tr',
17201             cn: [
17202             {
17203                 tag: 'th',
17204                 cls: 'prev',
17205                 html: '<i class="fa fa-arrow-left"/>'
17206             },
17207             {
17208                 tag: 'th',
17209                 cls: 'switch',
17210                 colspan: '5'
17211             },
17212             {
17213                 tag: 'th',
17214                 cls: 'next',
17215                 html: '<i class="fa fa-arrow-right"/>'
17216             }
17217
17218             ]
17219         }
17220         ]
17221     },
17222     
17223     content : {
17224         tag: 'tbody',
17225         cn: [
17226         {
17227             tag: 'tr',
17228             cn: [
17229             {
17230                 tag: 'td',
17231                 colspan: '7'
17232             }
17233             ]
17234         }
17235         ]
17236     },
17237     
17238     footer : {
17239         tag: 'tfoot',
17240         cn: [
17241         {
17242             tag: 'tr',
17243             cn: [
17244             {
17245                 tag: 'th',
17246                 colspan: '7',
17247                 cls: 'today'
17248             }
17249                     
17250             ]
17251         }
17252         ]
17253     },
17254     
17255     dates:{
17256         en: {
17257             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17258             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17259             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17260             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17261             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17262             today: "Today"
17263         }
17264     },
17265     
17266     modes: [
17267     {
17268         clsName: 'days',
17269         navFnc: 'Month',
17270         navStep: 1
17271     },
17272     {
17273         clsName: 'months',
17274         navFnc: 'FullYear',
17275         navStep: 1
17276     },
17277     {
17278         clsName: 'years',
17279         navFnc: 'FullYear',
17280         navStep: 10
17281     }]
17282 });
17283
17284 Roo.apply(Roo.bootstrap.DateField,  {
17285   
17286     template : {
17287         tag: 'div',
17288         cls: 'datepicker dropdown-menu roo-dynamic',
17289         cn: [
17290         {
17291             tag: 'div',
17292             cls: 'datepicker-days',
17293             cn: [
17294             {
17295                 tag: 'table',
17296                 cls: 'table-condensed',
17297                 cn:[
17298                 Roo.bootstrap.DateField.head,
17299                 {
17300                     tag: 'tbody'
17301                 },
17302                 Roo.bootstrap.DateField.footer
17303                 ]
17304             }
17305             ]
17306         },
17307         {
17308             tag: 'div',
17309             cls: 'datepicker-months',
17310             cn: [
17311             {
17312                 tag: 'table',
17313                 cls: 'table-condensed',
17314                 cn:[
17315                 Roo.bootstrap.DateField.head,
17316                 Roo.bootstrap.DateField.content,
17317                 Roo.bootstrap.DateField.footer
17318                 ]
17319             }
17320             ]
17321         },
17322         {
17323             tag: 'div',
17324             cls: 'datepicker-years',
17325             cn: [
17326             {
17327                 tag: 'table',
17328                 cls: 'table-condensed',
17329                 cn:[
17330                 Roo.bootstrap.DateField.head,
17331                 Roo.bootstrap.DateField.content,
17332                 Roo.bootstrap.DateField.footer
17333                 ]
17334             }
17335             ]
17336         }
17337         ]
17338     }
17339 });
17340
17341  
17342
17343  /*
17344  * - LGPL
17345  *
17346  * TimeField
17347  * 
17348  */
17349
17350 /**
17351  * @class Roo.bootstrap.TimeField
17352  * @extends Roo.bootstrap.Input
17353  * Bootstrap DateField class
17354  * 
17355  * 
17356  * @constructor
17357  * Create a new TimeField
17358  * @param {Object} config The config object
17359  */
17360
17361 Roo.bootstrap.TimeField = function(config){
17362     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17363     this.addEvents({
17364             /**
17365              * @event show
17366              * Fires when this field show.
17367              * @param {Roo.bootstrap.DateField} thisthis
17368              * @param {Mixed} date The date value
17369              */
17370             show : true,
17371             /**
17372              * @event show
17373              * Fires when this field hide.
17374              * @param {Roo.bootstrap.DateField} this
17375              * @param {Mixed} date The date value
17376              */
17377             hide : true,
17378             /**
17379              * @event select
17380              * Fires when select a date.
17381              * @param {Roo.bootstrap.DateField} this
17382              * @param {Mixed} date The date value
17383              */
17384             select : true
17385         });
17386 };
17387
17388 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17389     
17390     /**
17391      * @cfg {String} format
17392      * The default time format string which can be overriden for localization support.  The format must be
17393      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17394      */
17395     format : "H:i",
17396        
17397     onRender: function(ct, position)
17398     {
17399         
17400         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17401                 
17402         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17403         
17404         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17405         
17406         this.pop = this.picker().select('>.datepicker-time',true).first();
17407         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17408         
17409         this.picker().on('mousedown', this.onMousedown, this);
17410         this.picker().on('click', this.onClick, this);
17411         
17412         this.picker().addClass('datepicker-dropdown');
17413     
17414         this.fillTime();
17415         this.update();
17416             
17417         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17418         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17419         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17420         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17421         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17422         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17423
17424     },
17425     
17426     fireKey: function(e){
17427         if (!this.picker().isVisible()){
17428             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17429                 this.show();
17430             }
17431             return;
17432         }
17433
17434         e.preventDefault();
17435         
17436         switch(e.keyCode){
17437             case 27: // escape
17438                 this.hide();
17439                 break;
17440             case 37: // left
17441             case 39: // right
17442                 this.onTogglePeriod();
17443                 break;
17444             case 38: // up
17445                 this.onIncrementMinutes();
17446                 break;
17447             case 40: // down
17448                 this.onDecrementMinutes();
17449                 break;
17450             case 13: // enter
17451             case 9: // tab
17452                 this.setTime();
17453                 break;
17454         }
17455     },
17456     
17457     onClick: function(e) {
17458         e.stopPropagation();
17459         e.preventDefault();
17460     },
17461     
17462     picker : function()
17463     {
17464         return this.el.select('.datepicker', true).first();
17465     },
17466     
17467     fillTime: function()
17468     {    
17469         var time = this.pop.select('tbody', true).first();
17470         
17471         time.dom.innerHTML = '';
17472         
17473         time.createChild({
17474             tag: 'tr',
17475             cn: [
17476                 {
17477                     tag: 'td',
17478                     cn: [
17479                         {
17480                             tag: 'a',
17481                             href: '#',
17482                             cls: 'btn',
17483                             cn: [
17484                                 {
17485                                     tag: 'span',
17486                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17487                                 }
17488                             ]
17489                         } 
17490                     ]
17491                 },
17492                 {
17493                     tag: 'td',
17494                     cls: 'separator'
17495                 },
17496                 {
17497                     tag: 'td',
17498                     cn: [
17499                         {
17500                             tag: 'a',
17501                             href: '#',
17502                             cls: 'btn',
17503                             cn: [
17504                                 {
17505                                     tag: 'span',
17506                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17507                                 }
17508                             ]
17509                         }
17510                     ]
17511                 },
17512                 {
17513                     tag: 'td',
17514                     cls: 'separator'
17515                 }
17516             ]
17517         });
17518         
17519         time.createChild({
17520             tag: 'tr',
17521             cn: [
17522                 {
17523                     tag: 'td',
17524                     cn: [
17525                         {
17526                             tag: 'span',
17527                             cls: 'timepicker-hour',
17528                             html: '00'
17529                         }  
17530                     ]
17531                 },
17532                 {
17533                     tag: 'td',
17534                     cls: 'separator',
17535                     html: ':'
17536                 },
17537                 {
17538                     tag: 'td',
17539                     cn: [
17540                         {
17541                             tag: 'span',
17542                             cls: 'timepicker-minute',
17543                             html: '00'
17544                         }  
17545                     ]
17546                 },
17547                 {
17548                     tag: 'td',
17549                     cls: 'separator'
17550                 },
17551                 {
17552                     tag: 'td',
17553                     cn: [
17554                         {
17555                             tag: 'button',
17556                             type: 'button',
17557                             cls: 'btn btn-primary period',
17558                             html: 'AM'
17559                             
17560                         }
17561                     ]
17562                 }
17563             ]
17564         });
17565         
17566         time.createChild({
17567             tag: 'tr',
17568             cn: [
17569                 {
17570                     tag: 'td',
17571                     cn: [
17572                         {
17573                             tag: 'a',
17574                             href: '#',
17575                             cls: 'btn',
17576                             cn: [
17577                                 {
17578                                     tag: 'span',
17579                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17580                                 }
17581                             ]
17582                         }
17583                     ]
17584                 },
17585                 {
17586                     tag: 'td',
17587                     cls: 'separator'
17588                 },
17589                 {
17590                     tag: 'td',
17591                     cn: [
17592                         {
17593                             tag: 'a',
17594                             href: '#',
17595                             cls: 'btn',
17596                             cn: [
17597                                 {
17598                                     tag: 'span',
17599                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17600                                 }
17601                             ]
17602                         }
17603                     ]
17604                 },
17605                 {
17606                     tag: 'td',
17607                     cls: 'separator'
17608                 }
17609             ]
17610         });
17611         
17612     },
17613     
17614     update: function()
17615     {
17616         
17617         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17618         
17619         this.fill();
17620     },
17621     
17622     fill: function() 
17623     {
17624         var hours = this.time.getHours();
17625         var minutes = this.time.getMinutes();
17626         var period = 'AM';
17627         
17628         if(hours > 11){
17629             period = 'PM';
17630         }
17631         
17632         if(hours == 0){
17633             hours = 12;
17634         }
17635         
17636         
17637         if(hours > 12){
17638             hours = hours - 12;
17639         }
17640         
17641         if(hours < 10){
17642             hours = '0' + hours;
17643         }
17644         
17645         if(minutes < 10){
17646             minutes = '0' + minutes;
17647         }
17648         
17649         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17650         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17651         this.pop.select('button', true).first().dom.innerHTML = period;
17652         
17653     },
17654     
17655     place: function()
17656     {   
17657         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17658         
17659         var cls = ['bottom'];
17660         
17661         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17662             cls.pop();
17663             cls.push('top');
17664         }
17665         
17666         cls.push('right');
17667         
17668         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17669             cls.pop();
17670             cls.push('left');
17671         }
17672         
17673         this.picker().addClass(cls.join('-'));
17674         
17675         var _this = this;
17676         
17677         Roo.each(cls, function(c){
17678             if(c == 'bottom'){
17679                 _this.picker().setTop(_this.inputEl().getHeight());
17680                 return;
17681             }
17682             if(c == 'top'){
17683                 _this.picker().setTop(0 - _this.picker().getHeight());
17684                 return;
17685             }
17686             
17687             if(c == 'left'){
17688                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17689                 return;
17690             }
17691             if(c == 'right'){
17692                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17693                 return;
17694             }
17695         });
17696         
17697     },
17698   
17699     onFocus : function()
17700     {
17701         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17702         this.show();
17703     },
17704     
17705     onBlur : function()
17706     {
17707         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17708         this.hide();
17709     },
17710     
17711     show : function()
17712     {
17713         this.picker().show();
17714         this.pop.show();
17715         this.update();
17716         this.place();
17717         
17718         this.fireEvent('show', this, this.date);
17719     },
17720     
17721     hide : function()
17722     {
17723         this.picker().hide();
17724         this.pop.hide();
17725         
17726         this.fireEvent('hide', this, this.date);
17727     },
17728     
17729     setTime : function()
17730     {
17731         this.hide();
17732         this.setValue(this.time.format(this.format));
17733         
17734         this.fireEvent('select', this, this.date);
17735         
17736         
17737     },
17738     
17739     onMousedown: function(e){
17740         e.stopPropagation();
17741         e.preventDefault();
17742     },
17743     
17744     onIncrementHours: function()
17745     {
17746         Roo.log('onIncrementHours');
17747         this.time = this.time.add(Date.HOUR, 1);
17748         this.update();
17749         
17750     },
17751     
17752     onDecrementHours: function()
17753     {
17754         Roo.log('onDecrementHours');
17755         this.time = this.time.add(Date.HOUR, -1);
17756         this.update();
17757     },
17758     
17759     onIncrementMinutes: function()
17760     {
17761         Roo.log('onIncrementMinutes');
17762         this.time = this.time.add(Date.MINUTE, 1);
17763         this.update();
17764     },
17765     
17766     onDecrementMinutes: function()
17767     {
17768         Roo.log('onDecrementMinutes');
17769         this.time = this.time.add(Date.MINUTE, -1);
17770         this.update();
17771     },
17772     
17773     onTogglePeriod: function()
17774     {
17775         Roo.log('onTogglePeriod');
17776         this.time = this.time.add(Date.HOUR, 12);
17777         this.update();
17778     }
17779     
17780    
17781 });
17782
17783 Roo.apply(Roo.bootstrap.TimeField,  {
17784     
17785     content : {
17786         tag: 'tbody',
17787         cn: [
17788             {
17789                 tag: 'tr',
17790                 cn: [
17791                 {
17792                     tag: 'td',
17793                     colspan: '7'
17794                 }
17795                 ]
17796             }
17797         ]
17798     },
17799     
17800     footer : {
17801         tag: 'tfoot',
17802         cn: [
17803             {
17804                 tag: 'tr',
17805                 cn: [
17806                 {
17807                     tag: 'th',
17808                     colspan: '7',
17809                     cls: '',
17810                     cn: [
17811                         {
17812                             tag: 'button',
17813                             cls: 'btn btn-info ok',
17814                             html: 'OK'
17815                         }
17816                     ]
17817                 }
17818
17819                 ]
17820             }
17821         ]
17822     }
17823 });
17824
17825 Roo.apply(Roo.bootstrap.TimeField,  {
17826   
17827     template : {
17828         tag: 'div',
17829         cls: 'datepicker dropdown-menu',
17830         cn: [
17831             {
17832                 tag: 'div',
17833                 cls: 'datepicker-time',
17834                 cn: [
17835                 {
17836                     tag: 'table',
17837                     cls: 'table-condensed',
17838                     cn:[
17839                     Roo.bootstrap.TimeField.content,
17840                     Roo.bootstrap.TimeField.footer
17841                     ]
17842                 }
17843                 ]
17844             }
17845         ]
17846     }
17847 });
17848
17849  
17850
17851  /*
17852  * - LGPL
17853  *
17854  * MonthField
17855  * 
17856  */
17857
17858 /**
17859  * @class Roo.bootstrap.MonthField
17860  * @extends Roo.bootstrap.Input
17861  * Bootstrap MonthField class
17862  * 
17863  * @cfg {String} language default en
17864  * 
17865  * @constructor
17866  * Create a new MonthField
17867  * @param {Object} config The config object
17868  */
17869
17870 Roo.bootstrap.MonthField = function(config){
17871     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17872     
17873     this.addEvents({
17874         /**
17875          * @event show
17876          * Fires when this field show.
17877          * @param {Roo.bootstrap.MonthField} this
17878          * @param {Mixed} date The date value
17879          */
17880         show : true,
17881         /**
17882          * @event show
17883          * Fires when this field hide.
17884          * @param {Roo.bootstrap.MonthField} this
17885          * @param {Mixed} date The date value
17886          */
17887         hide : true,
17888         /**
17889          * @event select
17890          * Fires when select a date.
17891          * @param {Roo.bootstrap.MonthField} this
17892          * @param {String} oldvalue The old value
17893          * @param {String} newvalue The new value
17894          */
17895         select : true
17896     });
17897 };
17898
17899 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17900     
17901     onRender: function(ct, position)
17902     {
17903         
17904         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17905         
17906         this.language = this.language || 'en';
17907         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17908         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17909         
17910         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17911         this.isInline = false;
17912         this.isInput = true;
17913         this.component = this.el.select('.add-on', true).first() || false;
17914         this.component = (this.component && this.component.length === 0) ? false : this.component;
17915         this.hasInput = this.component && this.inputEL().length;
17916         
17917         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17918         
17919         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17920         
17921         this.picker().on('mousedown', this.onMousedown, this);
17922         this.picker().on('click', this.onClick, this);
17923         
17924         this.picker().addClass('datepicker-dropdown');
17925         
17926         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17927             v.setStyle('width', '189px');
17928         });
17929         
17930         this.fillMonths();
17931         
17932         this.update();
17933         
17934         if(this.isInline) {
17935             this.show();
17936         }
17937         
17938     },
17939     
17940     setValue: function(v, suppressEvent)
17941     {   
17942         var o = this.getValue();
17943         
17944         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17945         
17946         this.update();
17947
17948         if(suppressEvent !== true){
17949             this.fireEvent('select', this, o, v);
17950         }
17951         
17952     },
17953     
17954     getValue: function()
17955     {
17956         return this.value;
17957     },
17958     
17959     onClick: function(e) 
17960     {
17961         e.stopPropagation();
17962         e.preventDefault();
17963         
17964         var target = e.getTarget();
17965         
17966         if(target.nodeName.toLowerCase() === 'i'){
17967             target = Roo.get(target).dom.parentNode;
17968         }
17969         
17970         var nodeName = target.nodeName;
17971         var className = target.className;
17972         var html = target.innerHTML;
17973         
17974         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17975             return;
17976         }
17977         
17978         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17979         
17980         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17981         
17982         this.hide();
17983                         
17984     },
17985     
17986     picker : function()
17987     {
17988         return this.pickerEl;
17989     },
17990     
17991     fillMonths: function()
17992     {    
17993         var i = 0;
17994         var months = this.picker().select('>.datepicker-months td', true).first();
17995         
17996         months.dom.innerHTML = '';
17997         
17998         while (i < 12) {
17999             var month = {
18000                 tag: 'span',
18001                 cls: 'month',
18002                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18003             }
18004             
18005             months.createChild(month);
18006         }
18007         
18008     },
18009     
18010     update: function()
18011     {
18012         var _this = this;
18013         
18014         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18015             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18016         }
18017         
18018         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18019             e.removeClass('active');
18020             
18021             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18022                 e.addClass('active');
18023             }
18024         })
18025     },
18026     
18027     place: function()
18028     {
18029         if(this.isInline) return;
18030         
18031         this.picker().removeClass(['bottom', 'top']);
18032         
18033         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18034             /*
18035              * place to the top of element!
18036              *
18037              */
18038             
18039             this.picker().addClass('top');
18040             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18041             
18042             return;
18043         }
18044         
18045         this.picker().addClass('bottom');
18046         
18047         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18048     },
18049     
18050     onFocus : function()
18051     {
18052         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18053         this.show();
18054     },
18055     
18056     onBlur : function()
18057     {
18058         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18059         
18060         var d = this.inputEl().getValue();
18061         
18062         this.setValue(d);
18063                 
18064         this.hide();
18065     },
18066     
18067     show : function()
18068     {
18069         this.picker().show();
18070         this.picker().select('>.datepicker-months', true).first().show();
18071         this.update();
18072         this.place();
18073         
18074         this.fireEvent('show', this, this.date);
18075     },
18076     
18077     hide : function()
18078     {
18079         if(this.isInline) return;
18080         this.picker().hide();
18081         this.fireEvent('hide', this, this.date);
18082         
18083     },
18084     
18085     onMousedown: function(e)
18086     {
18087         e.stopPropagation();
18088         e.preventDefault();
18089     },
18090     
18091     keyup: function(e)
18092     {
18093         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18094         this.update();
18095     },
18096
18097     fireKey: function(e)
18098     {
18099         if (!this.picker().isVisible()){
18100             if (e.keyCode == 27) // allow escape to hide and re-show picker
18101                 this.show();
18102             return;
18103         }
18104         
18105         var dir;
18106         
18107         switch(e.keyCode){
18108             case 27: // escape
18109                 this.hide();
18110                 e.preventDefault();
18111                 break;
18112             case 37: // left
18113             case 39: // right
18114                 dir = e.keyCode == 37 ? -1 : 1;
18115                 
18116                 this.vIndex = this.vIndex + dir;
18117                 
18118                 if(this.vIndex < 0){
18119                     this.vIndex = 0;
18120                 }
18121                 
18122                 if(this.vIndex > 11){
18123                     this.vIndex = 11;
18124                 }
18125                 
18126                 if(isNaN(this.vIndex)){
18127                     this.vIndex = 0;
18128                 }
18129                 
18130                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18131                 
18132                 break;
18133             case 38: // up
18134             case 40: // down
18135                 
18136                 dir = e.keyCode == 38 ? -1 : 1;
18137                 
18138                 this.vIndex = this.vIndex + dir * 4;
18139                 
18140                 if(this.vIndex < 0){
18141                     this.vIndex = 0;
18142                 }
18143                 
18144                 if(this.vIndex > 11){
18145                     this.vIndex = 11;
18146                 }
18147                 
18148                 if(isNaN(this.vIndex)){
18149                     this.vIndex = 0;
18150                 }
18151                 
18152                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18153                 break;
18154                 
18155             case 13: // enter
18156                 
18157                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18158                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18159                 }
18160                 
18161                 this.hide();
18162                 e.preventDefault();
18163                 break;
18164             case 9: // tab
18165                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18166                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18167                 }
18168                 this.hide();
18169                 break;
18170             case 16: // shift
18171             case 17: // ctrl
18172             case 18: // alt
18173                 break;
18174             default :
18175                 this.hide();
18176                 
18177         }
18178     },
18179     
18180     remove: function() 
18181     {
18182         this.picker().remove();
18183     }
18184    
18185 });
18186
18187 Roo.apply(Roo.bootstrap.MonthField,  {
18188     
18189     content : {
18190         tag: 'tbody',
18191         cn: [
18192         {
18193             tag: 'tr',
18194             cn: [
18195             {
18196                 tag: 'td',
18197                 colspan: '7'
18198             }
18199             ]
18200         }
18201         ]
18202     },
18203     
18204     dates:{
18205         en: {
18206             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18207             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18208         }
18209     }
18210 });
18211
18212 Roo.apply(Roo.bootstrap.MonthField,  {
18213   
18214     template : {
18215         tag: 'div',
18216         cls: 'datepicker dropdown-menu roo-dynamic',
18217         cn: [
18218             {
18219                 tag: 'div',
18220                 cls: 'datepicker-months',
18221                 cn: [
18222                 {
18223                     tag: 'table',
18224                     cls: 'table-condensed',
18225                     cn:[
18226                         Roo.bootstrap.DateField.content
18227                     ]
18228                 }
18229                 ]
18230             }
18231         ]
18232     }
18233 });
18234
18235  
18236
18237  
18238  /*
18239  * - LGPL
18240  *
18241  * CheckBox
18242  * 
18243  */
18244
18245 /**
18246  * @class Roo.bootstrap.CheckBox
18247  * @extends Roo.bootstrap.Input
18248  * Bootstrap CheckBox class
18249  * 
18250  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18251  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18252  * @cfg {String} boxLabel The text that appears beside the checkbox
18253  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18254  * @cfg {Boolean} checked initnal the element
18255  * @cfg {Boolean} inline inline the element (default false)
18256  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18257  * 
18258  * @constructor
18259  * Create a new CheckBox
18260  * @param {Object} config The config object
18261  */
18262
18263 Roo.bootstrap.CheckBox = function(config){
18264     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18265    
18266     this.addEvents({
18267         /**
18268         * @event check
18269         * Fires when the element is checked or unchecked.
18270         * @param {Roo.bootstrap.CheckBox} this This input
18271         * @param {Boolean} checked The new checked value
18272         */
18273        check : true
18274     });
18275     
18276 };
18277
18278 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18279   
18280     inputType: 'checkbox',
18281     inputValue: 1,
18282     valueOff: 0,
18283     boxLabel: false,
18284     checked: false,
18285     weight : false,
18286     inline: false,
18287     
18288     getAutoCreate : function()
18289     {
18290         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18291         
18292         var id = Roo.id();
18293         
18294         var cfg = {};
18295         
18296         cfg.cls = 'form-group ' + this.inputType; //input-group
18297         
18298         if(this.inline){
18299             cfg.cls += ' ' + this.inputType + '-inline';
18300         }
18301         
18302         var input =  {
18303             tag: 'input',
18304             id : id,
18305             type : this.inputType,
18306             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18307             cls : 'roo-' + this.inputType, //'form-box',
18308             placeholder : this.placeholder || ''
18309             
18310         };
18311         
18312         if (this.weight) { // Validity check?
18313             cfg.cls += " " + this.inputType + "-" + this.weight;
18314         }
18315         
18316         if (this.disabled) {
18317             input.disabled=true;
18318         }
18319         
18320         if(this.checked){
18321             input.checked = this.checked;
18322         }
18323         
18324         if (this.name) {
18325             input.name = this.name;
18326         }
18327         
18328         if (this.size) {
18329             input.cls += ' input-' + this.size;
18330         }
18331         
18332         var settings=this;
18333         
18334         ['xs','sm','md','lg'].map(function(size){
18335             if (settings[size]) {
18336                 cfg.cls += ' col-' + size + '-' + settings[size];
18337             }
18338         });
18339         
18340         var inputblock = input;
18341          
18342         if (this.before || this.after) {
18343             
18344             inputblock = {
18345                 cls : 'input-group',
18346                 cn :  [] 
18347             };
18348             
18349             if (this.before) {
18350                 inputblock.cn.push({
18351                     tag :'span',
18352                     cls : 'input-group-addon',
18353                     html : this.before
18354                 });
18355             }
18356             
18357             inputblock.cn.push(input);
18358             
18359             if (this.after) {
18360                 inputblock.cn.push({
18361                     tag :'span',
18362                     cls : 'input-group-addon',
18363                     html : this.after
18364                 });
18365             }
18366             
18367         }
18368         
18369         if (align ==='left' && this.fieldLabel.length) {
18370                 Roo.log("left and has label");
18371                 cfg.cn = [
18372                     
18373                     {
18374                         tag: 'label',
18375                         'for' :  id,
18376                         cls : 'control-label col-md-' + this.labelWidth,
18377                         html : this.fieldLabel
18378                         
18379                     },
18380                     {
18381                         cls : "col-md-" + (12 - this.labelWidth), 
18382                         cn: [
18383                             inputblock
18384                         ]
18385                     }
18386                     
18387                 ];
18388         } else if ( this.fieldLabel.length) {
18389                 Roo.log(" label");
18390                 cfg.cn = [
18391                    
18392                     {
18393                         tag: this.boxLabel ? 'span' : 'label',
18394                         'for': id,
18395                         cls: 'control-label box-input-label',
18396                         //cls : 'input-group-addon',
18397                         html : this.fieldLabel
18398                         
18399                     },
18400                     
18401                     inputblock
18402                     
18403                 ];
18404
18405         } else {
18406             
18407                 Roo.log(" no label && no align");
18408                 cfg.cn = [  inputblock ] ;
18409                 
18410                 
18411         }
18412         if(this.boxLabel){
18413              var boxLabelCfg = {
18414                 tag: 'label',
18415                 //'for': id, // box label is handled by onclick - so no for...
18416                 cls: 'box-label',
18417                 html: this.boxLabel
18418             }
18419             
18420             if(this.tooltip){
18421                 boxLabelCfg.tooltip = this.tooltip;
18422             }
18423              
18424             cfg.cn.push(boxLabelCfg);
18425         }
18426         
18427         
18428        
18429         return cfg;
18430         
18431     },
18432     
18433     /**
18434      * return the real input element.
18435      */
18436     inputEl: function ()
18437     {
18438         return this.el.select('input.roo-' + this.inputType,true).first();
18439     },
18440     
18441     labelEl: function()
18442     {
18443         return this.el.select('label.control-label',true).first();
18444     },
18445     /* depricated... */
18446     
18447     label: function()
18448     {
18449         return this.labelEl();
18450     },
18451     
18452     initEvents : function()
18453     {
18454 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18455         
18456         this.inputEl().on('click', this.onClick,  this);
18457         
18458         if (this.boxLabel) { 
18459             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18460         }
18461         
18462         this.startValue = this.getValue();
18463         
18464         if(this.groupId){
18465             Roo.bootstrap.CheckBox.register(this);
18466         }
18467     },
18468     
18469     onClick : function()
18470     {   
18471         this.setChecked(!this.checked);
18472     },
18473     
18474     setChecked : function(state,suppressEvent)
18475     {
18476         this.startValue = this.getValue();
18477         
18478         if(this.inputType == 'radio'){
18479             
18480             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18481                 e.dom.checked = false;
18482             });
18483             
18484             this.inputEl().dom.checked = true;
18485             
18486             this.inputEl().dom.value = this.inputValue;
18487             
18488             if(suppressEvent !== true){
18489                 this.fireEvent('check', this, true);
18490             }
18491             
18492             this.validate();
18493             
18494             return;
18495         }
18496         
18497         this.checked = state;
18498         
18499         this.inputEl().dom.checked = state;
18500         
18501         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18502         
18503         if(suppressEvent !== true){
18504             this.fireEvent('check', this, state);
18505         }
18506         
18507         this.validate();
18508     },
18509     
18510     getValue : function()
18511     {
18512         if(this.inputType == 'radio'){
18513             return this.getGroupValue();
18514         }
18515         
18516         return this.inputEl().getValue();
18517         
18518     },
18519     
18520     getGroupValue : function()
18521     {
18522         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18523             return '';
18524         }
18525         
18526         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18527     },
18528     
18529     setValue : function(v,suppressEvent)
18530     {
18531         if(this.inputType == 'radio'){
18532             this.setGroupValue(v, suppressEvent);
18533             return;
18534         }
18535         
18536         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18537         
18538         this.validate();
18539     },
18540     
18541     setGroupValue : function(v, suppressEvent)
18542     {
18543         this.startValue = this.getValue();
18544         
18545         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18546             e.dom.checked = false;
18547             
18548             if(e.dom.value == v){
18549                 e.dom.checked = true;
18550             }
18551         });
18552         
18553         if(suppressEvent !== true){
18554             this.fireEvent('check', this, true);
18555         }
18556
18557         this.validate();
18558         
18559         return;
18560     },
18561     
18562     validate : function()
18563     {
18564         if(
18565                 this.disabled || 
18566                 (this.inputType == 'radio' && this.validateRadio()) ||
18567                 (this.inputType == 'checkbox' && this.validateCheckbox())
18568         ){
18569             this.markValid();
18570             return true;
18571         }
18572         
18573         this.markInvalid();
18574         return false;
18575     },
18576     
18577     validateRadio : function()
18578     {
18579         var valid = false;
18580         
18581         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18582             if(!e.dom.checked){
18583                 return;
18584             }
18585             
18586             valid = true;
18587             
18588             return false;
18589         });
18590         
18591         return valid;
18592     },
18593     
18594     validateCheckbox : function()
18595     {
18596         if(!this.groupId){
18597             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18598         }
18599         
18600         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18601         
18602         if(!group){
18603             return false;
18604         }
18605         
18606         var r = false;
18607         
18608         for(var i in group){
18609             if(r){
18610                 break;
18611             }
18612             
18613             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18614         }
18615         
18616         return r;
18617     },
18618     
18619     /**
18620      * Mark this field as valid
18621      */
18622     markValid : function()
18623     {
18624         if(this.allowBlank){
18625             return;
18626         }
18627         
18628         var _this = this;
18629         
18630         this.fireEvent('valid', this);
18631         
18632         if(this.inputType == 'radio'){
18633             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18634                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18635                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18636             });
18637             
18638             return;
18639         }
18640         
18641         if(!this.groupId){
18642             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18643             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18644             return;
18645         }
18646         
18647         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18648             
18649         if(!group){
18650             return;
18651         }
18652         
18653         for(var i in group){
18654             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18655             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18656         }
18657     },
18658     
18659      /**
18660      * Mark this field as invalid
18661      * @param {String} msg The validation message
18662      */
18663     markInvalid : function(msg)
18664     {
18665         if(this.allowBlank){
18666             return;
18667         }
18668         
18669         var _this = this;
18670         
18671         this.fireEvent('invalid', this, msg);
18672         
18673         if(this.inputType == 'radio'){
18674             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18675                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18676                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18677             });
18678             
18679             return;
18680         }
18681         
18682         if(!this.groupId){
18683             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18684             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18685             return;
18686         }
18687         
18688         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18689             
18690         if(!group){
18691             return;
18692         }
18693         
18694         for(var i in group){
18695             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18696             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18697         }
18698         
18699     }
18700     
18701 });
18702
18703 Roo.apply(Roo.bootstrap.CheckBox, {
18704     
18705     groups: {},
18706     
18707      /**
18708     * register a CheckBox Group
18709     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18710     */
18711     register : function(checkbox)
18712     {
18713         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18714             this.groups[checkbox.groupId] = {};
18715         }
18716         
18717         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18718             return;
18719         }
18720         
18721         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18722         
18723     },
18724     /**
18725     * fetch a CheckBox Group based on the group ID
18726     * @param {string} the group ID
18727     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18728     */
18729     get: function(groupId) {
18730         if (typeof(this.groups[groupId]) == 'undefined') {
18731             return false;
18732         }
18733         
18734         return this.groups[groupId] ;
18735     }
18736     
18737     
18738 });
18739 /*
18740  * - LGPL
18741  *
18742  * Radio
18743  *
18744  *
18745  * not inline
18746  *<div class="radio">
18747   <label>
18748     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18749     Option one is this and that&mdash;be sure to include why it's great
18750   </label>
18751 </div>
18752  *
18753  *
18754  *inline
18755  *<span>
18756  *<label class="radio-inline">fieldLabel</label>
18757  *<label class="radio-inline">
18758   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18759 </label>
18760 <span>
18761  * 
18762  * 
18763  */
18764
18765 /**
18766  * @class Roo.bootstrap.Radio
18767  * @extends Roo.bootstrap.CheckBox
18768  * Bootstrap Radio class
18769
18770  * @constructor
18771  * Create a new Radio
18772  * @param {Object} config The config object
18773  */
18774
18775 Roo.bootstrap.Radio = function(config){
18776     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18777    
18778 };
18779
18780 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18781     
18782     inputType: 'radio',
18783     inputValue: '',
18784     valueOff: '',
18785     
18786     getAutoCreate : function()
18787     {
18788         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18789         align = align || 'left'; // default...
18790         
18791         
18792         
18793         var id = Roo.id();
18794         
18795         var cfg = {
18796                 tag : this.inline ? 'span' : 'div',
18797                 cls : '',
18798                 cn : []
18799         };
18800         
18801         var inline = this.inline ? ' radio-inline' : '';
18802         
18803         var lbl = {
18804                 tag: 'label' ,
18805                 // does not need for, as we wrap the input with it..
18806                 'for' : id,
18807                 cls : 'control-label box-label' + inline,
18808                 cn : []
18809         };
18810         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18811         
18812         var fieldLabel = {
18813             tag: 'label' ,
18814             //cls : 'control-label' + inline,
18815             html : this.fieldLabel,
18816             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18817         };
18818         
18819  
18820         
18821         
18822         var input =  {
18823             tag: 'input',
18824             id : id,
18825             type : this.inputType,
18826             //value : (!this.checked) ? this.valueOff : this.inputValue,
18827             value : this.inputValue,
18828             cls : 'roo-radio',
18829             placeholder : this.placeholder || '' // ?? needed????
18830             
18831         };
18832         if (this.weight) { // Validity check?
18833             input.cls += " radio-" + this.weight;
18834         }
18835         if (this.disabled) {
18836             input.disabled=true;
18837         }
18838         
18839         if(this.checked){
18840             input.checked = this.checked;
18841         }
18842         
18843         if (this.name) {
18844             input.name = this.name;
18845         }
18846         
18847         if (this.size) {
18848             input.cls += ' input-' + this.size;
18849         }
18850         
18851         //?? can span's inline have a width??
18852         
18853         var settings=this;
18854         ['xs','sm','md','lg'].map(function(size){
18855             if (settings[size]) {
18856                 cfg.cls += ' col-' + size + '-' + settings[size];
18857             }
18858         });
18859         
18860         var inputblock = input;
18861         
18862         if (this.before || this.after) {
18863             
18864             inputblock = {
18865                 cls : 'input-group',
18866                 tag : 'span',
18867                 cn :  [] 
18868             };
18869             if (this.before) {
18870                 inputblock.cn.push({
18871                     tag :'span',
18872                     cls : 'input-group-addon',
18873                     html : this.before
18874                 });
18875             }
18876             inputblock.cn.push(input);
18877             if (this.after) {
18878                 inputblock.cn.push({
18879                     tag :'span',
18880                     cls : 'input-group-addon',
18881                     html : this.after
18882                 });
18883             }
18884             
18885         };
18886         
18887         
18888         if (this.fieldLabel && this.fieldLabel.length) {
18889             cfg.cn.push(fieldLabel);
18890         }
18891        
18892         // normal bootstrap puts the input inside the label.
18893         // however with our styled version - it has to go after the input.
18894        
18895         //lbl.cn.push(inputblock);
18896         
18897         var lblwrap =  {
18898             tag: 'span',
18899             cls: 'radio' + inline,
18900             cn: [
18901                 inputblock,
18902                 lbl
18903             ]
18904         };
18905         
18906         cfg.cn.push( lblwrap);
18907         
18908         if(this.boxLabel){
18909             lbl.cn.push({
18910                 tag: 'span',
18911                 html: this.boxLabel
18912             })
18913         }
18914          
18915         
18916         return cfg;
18917         
18918     },
18919     
18920     initEvents : function()
18921     {
18922 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18923         
18924         this.inputEl().on('click', this.onClick,  this);
18925         if (this.boxLabel) {
18926             Roo.log('find label')
18927             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18928         }
18929         
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         return this.el.select('input.roo-radio',true).first();
18935     },
18936     onClick : function()
18937     {   
18938         Roo.log("click");
18939         this.setChecked(true);
18940     },
18941     
18942     setChecked : function(state,suppressEvent)
18943     {
18944         if(state){
18945             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18946                 v.dom.checked = false;
18947             });
18948         }
18949         Roo.log(this.inputEl().dom);
18950         this.checked = state;
18951         this.inputEl().dom.checked = state;
18952         
18953         if(suppressEvent !== true){
18954             this.fireEvent('check', this, state);
18955         }
18956         
18957         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18958         
18959     },
18960     
18961     getGroupValue : function()
18962     {
18963         var value = '';
18964         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18965             if(v.dom.checked == true){
18966                 value = v.dom.value;
18967             }
18968         });
18969         
18970         return value;
18971     },
18972     
18973     /**
18974      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18975      * @return {Mixed} value The field value
18976      */
18977     getValue : function(){
18978         return this.getGroupValue();
18979     }
18980     
18981 });
18982
18983  
18984 //<script type="text/javascript">
18985
18986 /*
18987  * Based  Ext JS Library 1.1.1
18988  * Copyright(c) 2006-2007, Ext JS, LLC.
18989  * LGPL
18990  *
18991  */
18992  
18993 /**
18994  * @class Roo.HtmlEditorCore
18995  * @extends Roo.Component
18996  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18997  *
18998  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18999  */
19000
19001 Roo.HtmlEditorCore = function(config){
19002     
19003     
19004     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19005     
19006     
19007     this.addEvents({
19008         /**
19009          * @event initialize
19010          * Fires when the editor is fully initialized (including the iframe)
19011          * @param {Roo.HtmlEditorCore} this
19012          */
19013         initialize: true,
19014         /**
19015          * @event activate
19016          * Fires when the editor is first receives the focus. Any insertion must wait
19017          * until after this event.
19018          * @param {Roo.HtmlEditorCore} this
19019          */
19020         activate: true,
19021          /**
19022          * @event beforesync
19023          * Fires before the textarea is updated with content from the editor iframe. Return false
19024          * to cancel the sync.
19025          * @param {Roo.HtmlEditorCore} this
19026          * @param {String} html
19027          */
19028         beforesync: true,
19029          /**
19030          * @event beforepush
19031          * Fires before the iframe editor is updated with content from the textarea. Return false
19032          * to cancel the push.
19033          * @param {Roo.HtmlEditorCore} this
19034          * @param {String} html
19035          */
19036         beforepush: true,
19037          /**
19038          * @event sync
19039          * Fires when the textarea is updated with content from the editor iframe.
19040          * @param {Roo.HtmlEditorCore} this
19041          * @param {String} html
19042          */
19043         sync: true,
19044          /**
19045          * @event push
19046          * Fires when the iframe editor is updated with content from the textarea.
19047          * @param {Roo.HtmlEditorCore} this
19048          * @param {String} html
19049          */
19050         push: true,
19051         
19052         /**
19053          * @event editorevent
19054          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19055          * @param {Roo.HtmlEditorCore} this
19056          */
19057         editorevent: true
19058         
19059     });
19060     
19061     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19062     
19063     // defaults : white / black...
19064     this.applyBlacklists();
19065     
19066     
19067     
19068 };
19069
19070
19071 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19072
19073
19074      /**
19075      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19076      */
19077     
19078     owner : false,
19079     
19080      /**
19081      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19082      *                        Roo.resizable.
19083      */
19084     resizable : false,
19085      /**
19086      * @cfg {Number} height (in pixels)
19087      */   
19088     height: 300,
19089    /**
19090      * @cfg {Number} width (in pixels)
19091      */   
19092     width: 500,
19093     
19094     /**
19095      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19096      * 
19097      */
19098     stylesheets: false,
19099     
19100     // id of frame..
19101     frameId: false,
19102     
19103     // private properties
19104     validationEvent : false,
19105     deferHeight: true,
19106     initialized : false,
19107     activated : false,
19108     sourceEditMode : false,
19109     onFocus : Roo.emptyFn,
19110     iframePad:3,
19111     hideMode:'offsets',
19112     
19113     clearUp: true,
19114     
19115     // blacklist + whitelisted elements..
19116     black: false,
19117     white: false,
19118      
19119     
19120
19121     /**
19122      * Protected method that will not generally be called directly. It
19123      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19124      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19125      */
19126     getDocMarkup : function(){
19127         // body styles..
19128         var st = '';
19129         
19130         // inherit styels from page...?? 
19131         if (this.stylesheets === false) {
19132             
19133             Roo.get(document.head).select('style').each(function(node) {
19134                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19135             });
19136             
19137             Roo.get(document.head).select('link').each(function(node) { 
19138                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19139             });
19140             
19141         } else if (!this.stylesheets.length) {
19142                 // simple..
19143                 st = '<style type="text/css">' +
19144                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19145                    '</style>';
19146         } else { 
19147             
19148         }
19149         
19150         st +=  '<style type="text/css">' +
19151             'IMG { cursor: pointer } ' +
19152         '</style>';
19153
19154         
19155         return '<html><head>' + st  +
19156             //<style type="text/css">' +
19157             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19158             //'</style>' +
19159             ' </head><body class="roo-htmleditor-body"></body></html>';
19160     },
19161
19162     // private
19163     onRender : function(ct, position)
19164     {
19165         var _t = this;
19166         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19167         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19168         
19169         
19170         this.el.dom.style.border = '0 none';
19171         this.el.dom.setAttribute('tabIndex', -1);
19172         this.el.addClass('x-hidden hide');
19173         
19174         
19175         
19176         if(Roo.isIE){ // fix IE 1px bogus margin
19177             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19178         }
19179        
19180         
19181         this.frameId = Roo.id();
19182         
19183          
19184         
19185         var iframe = this.owner.wrap.createChild({
19186             tag: 'iframe',
19187             cls: 'form-control', // bootstrap..
19188             id: this.frameId,
19189             name: this.frameId,
19190             frameBorder : 'no',
19191             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19192         }, this.el
19193         );
19194         
19195         
19196         this.iframe = iframe.dom;
19197
19198          this.assignDocWin();
19199         
19200         this.doc.designMode = 'on';
19201        
19202         this.doc.open();
19203         this.doc.write(this.getDocMarkup());
19204         this.doc.close();
19205
19206         
19207         var task = { // must defer to wait for browser to be ready
19208             run : function(){
19209                 //console.log("run task?" + this.doc.readyState);
19210                 this.assignDocWin();
19211                 if(this.doc.body || this.doc.readyState == 'complete'){
19212                     try {
19213                         this.doc.designMode="on";
19214                     } catch (e) {
19215                         return;
19216                     }
19217                     Roo.TaskMgr.stop(task);
19218                     this.initEditor.defer(10, this);
19219                 }
19220             },
19221             interval : 10,
19222             duration: 10000,
19223             scope: this
19224         };
19225         Roo.TaskMgr.start(task);
19226
19227     },
19228
19229     // private
19230     onResize : function(w, h)
19231     {
19232          Roo.log('resize: ' +w + ',' + h );
19233         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19234         if(!this.iframe){
19235             return;
19236         }
19237         if(typeof w == 'number'){
19238             
19239             this.iframe.style.width = w + 'px';
19240         }
19241         if(typeof h == 'number'){
19242             
19243             this.iframe.style.height = h + 'px';
19244             if(this.doc){
19245                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19246             }
19247         }
19248         
19249     },
19250
19251     /**
19252      * Toggles the editor between standard and source edit mode.
19253      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19254      */
19255     toggleSourceEdit : function(sourceEditMode){
19256         
19257         this.sourceEditMode = sourceEditMode === true;
19258         
19259         if(this.sourceEditMode){
19260  
19261             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19262             
19263         }else{
19264             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19265             //this.iframe.className = '';
19266             this.deferFocus();
19267         }
19268         //this.setSize(this.owner.wrap.getSize());
19269         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19270     },
19271
19272     
19273   
19274
19275     /**
19276      * Protected method that will not generally be called directly. If you need/want
19277      * custom HTML cleanup, this is the method you should override.
19278      * @param {String} html The HTML to be cleaned
19279      * return {String} The cleaned HTML
19280      */
19281     cleanHtml : function(html){
19282         html = String(html);
19283         if(html.length > 5){
19284             if(Roo.isSafari){ // strip safari nonsense
19285                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19286             }
19287         }
19288         if(html == '&nbsp;'){
19289             html = '';
19290         }
19291         return html;
19292     },
19293
19294     /**
19295      * HTML Editor -> Textarea
19296      * Protected method that will not generally be called directly. Syncs the contents
19297      * of the editor iframe with the textarea.
19298      */
19299     syncValue : function(){
19300         if(this.initialized){
19301             var bd = (this.doc.body || this.doc.documentElement);
19302             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19303             var html = bd.innerHTML;
19304             if(Roo.isSafari){
19305                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19306                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19307                 if(m && m[1]){
19308                     html = '<div style="'+m[0]+'">' + html + '</div>';
19309                 }
19310             }
19311             html = this.cleanHtml(html);
19312             // fix up the special chars.. normaly like back quotes in word...
19313             // however we do not want to do this with chinese..
19314             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19315                 var cc = b.charCodeAt();
19316                 if (
19317                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19318                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19319                     (cc >= 0xf900 && cc < 0xfb00 )
19320                 ) {
19321                         return b;
19322                 }
19323                 return "&#"+cc+";" 
19324             });
19325             if(this.owner.fireEvent('beforesync', this, html) !== false){
19326                 this.el.dom.value = html;
19327                 this.owner.fireEvent('sync', this, html);
19328             }
19329         }
19330     },
19331
19332     /**
19333      * Protected method that will not generally be called directly. Pushes the value of the textarea
19334      * into the iframe editor.
19335      */
19336     pushValue : function(){
19337         if(this.initialized){
19338             var v = this.el.dom.value.trim();
19339             
19340 //            if(v.length < 1){
19341 //                v = '&#160;';
19342 //            }
19343             
19344             if(this.owner.fireEvent('beforepush', this, v) !== false){
19345                 var d = (this.doc.body || this.doc.documentElement);
19346                 d.innerHTML = v;
19347                 this.cleanUpPaste();
19348                 this.el.dom.value = d.innerHTML;
19349                 this.owner.fireEvent('push', this, v);
19350             }
19351         }
19352     },
19353
19354     // private
19355     deferFocus : function(){
19356         this.focus.defer(10, this);
19357     },
19358
19359     // doc'ed in Field
19360     focus : function(){
19361         if(this.win && !this.sourceEditMode){
19362             this.win.focus();
19363         }else{
19364             this.el.focus();
19365         }
19366     },
19367     
19368     assignDocWin: function()
19369     {
19370         var iframe = this.iframe;
19371         
19372          if(Roo.isIE){
19373             this.doc = iframe.contentWindow.document;
19374             this.win = iframe.contentWindow;
19375         } else {
19376 //            if (!Roo.get(this.frameId)) {
19377 //                return;
19378 //            }
19379 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19380 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19381             
19382             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19383                 return;
19384             }
19385             
19386             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19387             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19388         }
19389     },
19390     
19391     // private
19392     initEditor : function(){
19393         //console.log("INIT EDITOR");
19394         this.assignDocWin();
19395         
19396         
19397         
19398         this.doc.designMode="on";
19399         this.doc.open();
19400         this.doc.write(this.getDocMarkup());
19401         this.doc.close();
19402         
19403         var dbody = (this.doc.body || this.doc.documentElement);
19404         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19405         // this copies styles from the containing element into thsi one..
19406         // not sure why we need all of this..
19407         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19408         
19409         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19410         //ss['background-attachment'] = 'fixed'; // w3c
19411         dbody.bgProperties = 'fixed'; // ie
19412         //Roo.DomHelper.applyStyles(dbody, ss);
19413         Roo.EventManager.on(this.doc, {
19414             //'mousedown': this.onEditorEvent,
19415             'mouseup': this.onEditorEvent,
19416             'dblclick': this.onEditorEvent,
19417             'click': this.onEditorEvent,
19418             'keyup': this.onEditorEvent,
19419             buffer:100,
19420             scope: this
19421         });
19422         if(Roo.isGecko){
19423             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19424         }
19425         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19426             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19427         }
19428         this.initialized = true;
19429
19430         this.owner.fireEvent('initialize', this);
19431         this.pushValue();
19432     },
19433
19434     // private
19435     onDestroy : function(){
19436         
19437         
19438         
19439         if(this.rendered){
19440             
19441             //for (var i =0; i < this.toolbars.length;i++) {
19442             //    // fixme - ask toolbars for heights?
19443             //    this.toolbars[i].onDestroy();
19444            // }
19445             
19446             //this.wrap.dom.innerHTML = '';
19447             //this.wrap.remove();
19448         }
19449     },
19450
19451     // private
19452     onFirstFocus : function(){
19453         
19454         this.assignDocWin();
19455         
19456         
19457         this.activated = true;
19458          
19459     
19460         if(Roo.isGecko){ // prevent silly gecko errors
19461             this.win.focus();
19462             var s = this.win.getSelection();
19463             if(!s.focusNode || s.focusNode.nodeType != 3){
19464                 var r = s.getRangeAt(0);
19465                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19466                 r.collapse(true);
19467                 this.deferFocus();
19468             }
19469             try{
19470                 this.execCmd('useCSS', true);
19471                 this.execCmd('styleWithCSS', false);
19472             }catch(e){}
19473         }
19474         this.owner.fireEvent('activate', this);
19475     },
19476
19477     // private
19478     adjustFont: function(btn){
19479         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19480         //if(Roo.isSafari){ // safari
19481         //    adjust *= 2;
19482        // }
19483         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19484         if(Roo.isSafari){ // safari
19485             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19486             v =  (v < 10) ? 10 : v;
19487             v =  (v > 48) ? 48 : v;
19488             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19489             
19490         }
19491         
19492         
19493         v = Math.max(1, v+adjust);
19494         
19495         this.execCmd('FontSize', v  );
19496     },
19497
19498     onEditorEvent : function(e)
19499     {
19500         this.owner.fireEvent('editorevent', this, e);
19501       //  this.updateToolbar();
19502         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19503     },
19504
19505     insertTag : function(tg)
19506     {
19507         // could be a bit smarter... -> wrap the current selected tRoo..
19508         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19509             
19510             range = this.createRange(this.getSelection());
19511             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19512             wrappingNode.appendChild(range.extractContents());
19513             range.insertNode(wrappingNode);
19514
19515             return;
19516             
19517             
19518             
19519         }
19520         this.execCmd("formatblock",   tg);
19521         
19522     },
19523     
19524     insertText : function(txt)
19525     {
19526         
19527         
19528         var range = this.createRange();
19529         range.deleteContents();
19530                //alert(Sender.getAttribute('label'));
19531                
19532         range.insertNode(this.doc.createTextNode(txt));
19533     } ,
19534     
19535      
19536
19537     /**
19538      * Executes a Midas editor command on the editor document and performs necessary focus and
19539      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19540      * @param {String} cmd The Midas command
19541      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19542      */
19543     relayCmd : function(cmd, value){
19544         this.win.focus();
19545         this.execCmd(cmd, value);
19546         this.owner.fireEvent('editorevent', this);
19547         //this.updateToolbar();
19548         this.owner.deferFocus();
19549     },
19550
19551     /**
19552      * Executes a Midas editor command directly on the editor document.
19553      * For visual commands, you should use {@link #relayCmd} instead.
19554      * <b>This should only be called after the editor is initialized.</b>
19555      * @param {String} cmd The Midas command
19556      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19557      */
19558     execCmd : function(cmd, value){
19559         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19560         this.syncValue();
19561     },
19562  
19563  
19564    
19565     /**
19566      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19567      * to insert tRoo.
19568      * @param {String} text | dom node.. 
19569      */
19570     insertAtCursor : function(text)
19571     {
19572         
19573         
19574         
19575         if(!this.activated){
19576             return;
19577         }
19578         /*
19579         if(Roo.isIE){
19580             this.win.focus();
19581             var r = this.doc.selection.createRange();
19582             if(r){
19583                 r.collapse(true);
19584                 r.pasteHTML(text);
19585                 this.syncValue();
19586                 this.deferFocus();
19587             
19588             }
19589             return;
19590         }
19591         */
19592         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19593             this.win.focus();
19594             
19595             
19596             // from jquery ui (MIT licenced)
19597             var range, node;
19598             var win = this.win;
19599             
19600             if (win.getSelection && win.getSelection().getRangeAt) {
19601                 range = win.getSelection().getRangeAt(0);
19602                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19603                 range.insertNode(node);
19604             } else if (win.document.selection && win.document.selection.createRange) {
19605                 // no firefox support
19606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19607                 win.document.selection.createRange().pasteHTML(txt);
19608             } else {
19609                 // no firefox support
19610                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19611                 this.execCmd('InsertHTML', txt);
19612             } 
19613             
19614             this.syncValue();
19615             
19616             this.deferFocus();
19617         }
19618     },
19619  // private
19620     mozKeyPress : function(e){
19621         if(e.ctrlKey){
19622             var c = e.getCharCode(), cmd;
19623           
19624             if(c > 0){
19625                 c = String.fromCharCode(c).toLowerCase();
19626                 switch(c){
19627                     case 'b':
19628                         cmd = 'bold';
19629                         break;
19630                     case 'i':
19631                         cmd = 'italic';
19632                         break;
19633                     
19634                     case 'u':
19635                         cmd = 'underline';
19636                         break;
19637                     
19638                     case 'v':
19639                         this.cleanUpPaste.defer(100, this);
19640                         return;
19641                         
19642                 }
19643                 if(cmd){
19644                     this.win.focus();
19645                     this.execCmd(cmd);
19646                     this.deferFocus();
19647                     e.preventDefault();
19648                 }
19649                 
19650             }
19651         }
19652     },
19653
19654     // private
19655     fixKeys : function(){ // load time branching for fastest keydown performance
19656         if(Roo.isIE){
19657             return function(e){
19658                 var k = e.getKey(), r;
19659                 if(k == e.TAB){
19660                     e.stopEvent();
19661                     r = this.doc.selection.createRange();
19662                     if(r){
19663                         r.collapse(true);
19664                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19665                         this.deferFocus();
19666                     }
19667                     return;
19668                 }
19669                 
19670                 if(k == e.ENTER){
19671                     r = this.doc.selection.createRange();
19672                     if(r){
19673                         var target = r.parentElement();
19674                         if(!target || target.tagName.toLowerCase() != 'li'){
19675                             e.stopEvent();
19676                             r.pasteHTML('<br />');
19677                             r.collapse(false);
19678                             r.select();
19679                         }
19680                     }
19681                 }
19682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19683                     this.cleanUpPaste.defer(100, this);
19684                     return;
19685                 }
19686                 
19687                 
19688             };
19689         }else if(Roo.isOpera){
19690             return function(e){
19691                 var k = e.getKey();
19692                 if(k == e.TAB){
19693                     e.stopEvent();
19694                     this.win.focus();
19695                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19696                     this.deferFocus();
19697                 }
19698                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19699                     this.cleanUpPaste.defer(100, this);
19700                     return;
19701                 }
19702                 
19703             };
19704         }else if(Roo.isSafari){
19705             return function(e){
19706                 var k = e.getKey();
19707                 
19708                 if(k == e.TAB){
19709                     e.stopEvent();
19710                     this.execCmd('InsertText','\t');
19711                     this.deferFocus();
19712                     return;
19713                 }
19714                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19715                     this.cleanUpPaste.defer(100, this);
19716                     return;
19717                 }
19718                 
19719              };
19720         }
19721     }(),
19722     
19723     getAllAncestors: function()
19724     {
19725         var p = this.getSelectedNode();
19726         var a = [];
19727         if (!p) {
19728             a.push(p); // push blank onto stack..
19729             p = this.getParentElement();
19730         }
19731         
19732         
19733         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19734             a.push(p);
19735             p = p.parentNode;
19736         }
19737         a.push(this.doc.body);
19738         return a;
19739     },
19740     lastSel : false,
19741     lastSelNode : false,
19742     
19743     
19744     getSelection : function() 
19745     {
19746         this.assignDocWin();
19747         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19748     },
19749     
19750     getSelectedNode: function() 
19751     {
19752         // this may only work on Gecko!!!
19753         
19754         // should we cache this!!!!
19755         
19756         
19757         
19758          
19759         var range = this.createRange(this.getSelection()).cloneRange();
19760         
19761         if (Roo.isIE) {
19762             var parent = range.parentElement();
19763             while (true) {
19764                 var testRange = range.duplicate();
19765                 testRange.moveToElementText(parent);
19766                 if (testRange.inRange(range)) {
19767                     break;
19768                 }
19769                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19770                     break;
19771                 }
19772                 parent = parent.parentElement;
19773             }
19774             return parent;
19775         }
19776         
19777         // is ancestor a text element.
19778         var ac =  range.commonAncestorContainer;
19779         if (ac.nodeType == 3) {
19780             ac = ac.parentNode;
19781         }
19782         
19783         var ar = ac.childNodes;
19784          
19785         var nodes = [];
19786         var other_nodes = [];
19787         var has_other_nodes = false;
19788         for (var i=0;i<ar.length;i++) {
19789             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19790                 continue;
19791             }
19792             // fullly contained node.
19793             
19794             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19795                 nodes.push(ar[i]);
19796                 continue;
19797             }
19798             
19799             // probably selected..
19800             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19801                 other_nodes.push(ar[i]);
19802                 continue;
19803             }
19804             // outer..
19805             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19806                 continue;
19807             }
19808             
19809             
19810             has_other_nodes = true;
19811         }
19812         if (!nodes.length && other_nodes.length) {
19813             nodes= other_nodes;
19814         }
19815         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19816             return false;
19817         }
19818         
19819         return nodes[0];
19820     },
19821     createRange: function(sel)
19822     {
19823         // this has strange effects when using with 
19824         // top toolbar - not sure if it's a great idea.
19825         //this.editor.contentWindow.focus();
19826         if (typeof sel != "undefined") {
19827             try {
19828                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19829             } catch(e) {
19830                 return this.doc.createRange();
19831             }
19832         } else {
19833             return this.doc.createRange();
19834         }
19835     },
19836     getParentElement: function()
19837     {
19838         
19839         this.assignDocWin();
19840         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19841         
19842         var range = this.createRange(sel);
19843          
19844         try {
19845             var p = range.commonAncestorContainer;
19846             while (p.nodeType == 3) { // text node
19847                 p = p.parentNode;
19848             }
19849             return p;
19850         } catch (e) {
19851             return null;
19852         }
19853     
19854     },
19855     /***
19856      *
19857      * Range intersection.. the hard stuff...
19858      *  '-1' = before
19859      *  '0' = hits..
19860      *  '1' = after.
19861      *         [ -- selected range --- ]
19862      *   [fail]                        [fail]
19863      *
19864      *    basically..
19865      *      if end is before start or  hits it. fail.
19866      *      if start is after end or hits it fail.
19867      *
19868      *   if either hits (but other is outside. - then it's not 
19869      *   
19870      *    
19871      **/
19872     
19873     
19874     // @see http://www.thismuchiknow.co.uk/?p=64.
19875     rangeIntersectsNode : function(range, node)
19876     {
19877         var nodeRange = node.ownerDocument.createRange();
19878         try {
19879             nodeRange.selectNode(node);
19880         } catch (e) {
19881             nodeRange.selectNodeContents(node);
19882         }
19883     
19884         var rangeStartRange = range.cloneRange();
19885         rangeStartRange.collapse(true);
19886     
19887         var rangeEndRange = range.cloneRange();
19888         rangeEndRange.collapse(false);
19889     
19890         var nodeStartRange = nodeRange.cloneRange();
19891         nodeStartRange.collapse(true);
19892     
19893         var nodeEndRange = nodeRange.cloneRange();
19894         nodeEndRange.collapse(false);
19895     
19896         return rangeStartRange.compareBoundaryPoints(
19897                  Range.START_TO_START, nodeEndRange) == -1 &&
19898                rangeEndRange.compareBoundaryPoints(
19899                  Range.START_TO_START, nodeStartRange) == 1;
19900         
19901          
19902     },
19903     rangeCompareNode : function(range, node)
19904     {
19905         var nodeRange = node.ownerDocument.createRange();
19906         try {
19907             nodeRange.selectNode(node);
19908         } catch (e) {
19909             nodeRange.selectNodeContents(node);
19910         }
19911         
19912         
19913         range.collapse(true);
19914     
19915         nodeRange.collapse(true);
19916      
19917         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19918         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19919          
19920         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19921         
19922         var nodeIsBefore   =  ss == 1;
19923         var nodeIsAfter    = ee == -1;
19924         
19925         if (nodeIsBefore && nodeIsAfter)
19926             return 0; // outer
19927         if (!nodeIsBefore && nodeIsAfter)
19928             return 1; //right trailed.
19929         
19930         if (nodeIsBefore && !nodeIsAfter)
19931             return 2;  // left trailed.
19932         // fully contined.
19933         return 3;
19934     },
19935
19936     // private? - in a new class?
19937     cleanUpPaste :  function()
19938     {
19939         // cleans up the whole document..
19940         Roo.log('cleanuppaste');
19941         
19942         this.cleanUpChildren(this.doc.body);
19943         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19944         if (clean != this.doc.body.innerHTML) {
19945             this.doc.body.innerHTML = clean;
19946         }
19947         
19948     },
19949     
19950     cleanWordChars : function(input) {// change the chars to hex code
19951         var he = Roo.HtmlEditorCore;
19952         
19953         var output = input;
19954         Roo.each(he.swapCodes, function(sw) { 
19955             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19956             
19957             output = output.replace(swapper, sw[1]);
19958         });
19959         
19960         return output;
19961     },
19962     
19963     
19964     cleanUpChildren : function (n)
19965     {
19966         if (!n.childNodes.length) {
19967             return;
19968         }
19969         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19970            this.cleanUpChild(n.childNodes[i]);
19971         }
19972     },
19973     
19974     
19975         
19976     
19977     cleanUpChild : function (node)
19978     {
19979         var ed = this;
19980         //console.log(node);
19981         if (node.nodeName == "#text") {
19982             // clean up silly Windows -- stuff?
19983             return; 
19984         }
19985         if (node.nodeName == "#comment") {
19986             node.parentNode.removeChild(node);
19987             // clean up silly Windows -- stuff?
19988             return; 
19989         }
19990         var lcname = node.tagName.toLowerCase();
19991         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19992         // whitelist of tags..
19993         
19994         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19995             // remove node.
19996             node.parentNode.removeChild(node);
19997             return;
19998             
19999         }
20000         
20001         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20002         
20003         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20004         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20005         
20006         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20007         //    remove_keep_children = true;
20008         //}
20009         
20010         if (remove_keep_children) {
20011             this.cleanUpChildren(node);
20012             // inserts everything just before this node...
20013             while (node.childNodes.length) {
20014                 var cn = node.childNodes[0];
20015                 node.removeChild(cn);
20016                 node.parentNode.insertBefore(cn, node);
20017             }
20018             node.parentNode.removeChild(node);
20019             return;
20020         }
20021         
20022         if (!node.attributes || !node.attributes.length) {
20023             this.cleanUpChildren(node);
20024             return;
20025         }
20026         
20027         function cleanAttr(n,v)
20028         {
20029             
20030             if (v.match(/^\./) || v.match(/^\//)) {
20031                 return;
20032             }
20033             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20034                 return;
20035             }
20036             if (v.match(/^#/)) {
20037                 return;
20038             }
20039 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20040             node.removeAttribute(n);
20041             
20042         }
20043         
20044         var cwhite = this.cwhite;
20045         var cblack = this.cblack;
20046             
20047         function cleanStyle(n,v)
20048         {
20049             if (v.match(/expression/)) { //XSS?? should we even bother..
20050                 node.removeAttribute(n);
20051                 return;
20052             }
20053             
20054             var parts = v.split(/;/);
20055             var clean = [];
20056             
20057             Roo.each(parts, function(p) {
20058                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20059                 if (!p.length) {
20060                     return true;
20061                 }
20062                 var l = p.split(':').shift().replace(/\s+/g,'');
20063                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20064                 
20065                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20066 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20067                     //node.removeAttribute(n);
20068                     return true;
20069                 }
20070                 //Roo.log()
20071                 // only allow 'c whitelisted system attributes'
20072                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20073 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20074                     //node.removeAttribute(n);
20075                     return true;
20076                 }
20077                 
20078                 
20079                  
20080                 
20081                 clean.push(p);
20082                 return true;
20083             });
20084             if (clean.length) { 
20085                 node.setAttribute(n, clean.join(';'));
20086             } else {
20087                 node.removeAttribute(n);
20088             }
20089             
20090         }
20091         
20092         
20093         for (var i = node.attributes.length-1; i > -1 ; i--) {
20094             var a = node.attributes[i];
20095             //console.log(a);
20096             
20097             if (a.name.toLowerCase().substr(0,2)=='on')  {
20098                 node.removeAttribute(a.name);
20099                 continue;
20100             }
20101             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20102                 node.removeAttribute(a.name);
20103                 continue;
20104             }
20105             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20106                 cleanAttr(a.name,a.value); // fixme..
20107                 continue;
20108             }
20109             if (a.name == 'style') {
20110                 cleanStyle(a.name,a.value);
20111                 continue;
20112             }
20113             /// clean up MS crap..
20114             // tecnically this should be a list of valid class'es..
20115             
20116             
20117             if (a.name == 'class') {
20118                 if (a.value.match(/^Mso/)) {
20119                     node.className = '';
20120                 }
20121                 
20122                 if (a.value.match(/body/)) {
20123                     node.className = '';
20124                 }
20125                 continue;
20126             }
20127             
20128             // style cleanup!?
20129             // class cleanup?
20130             
20131         }
20132         
20133         
20134         this.cleanUpChildren(node);
20135         
20136         
20137     },
20138     
20139     /**
20140      * Clean up MS wordisms...
20141      */
20142     cleanWord : function(node)
20143     {
20144         
20145         
20146         if (!node) {
20147             this.cleanWord(this.doc.body);
20148             return;
20149         }
20150         if (node.nodeName == "#text") {
20151             // clean up silly Windows -- stuff?
20152             return; 
20153         }
20154         if (node.nodeName == "#comment") {
20155             node.parentNode.removeChild(node);
20156             // clean up silly Windows -- stuff?
20157             return; 
20158         }
20159         
20160         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20161             node.parentNode.removeChild(node);
20162             return;
20163         }
20164         
20165         // remove - but keep children..
20166         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20167             while (node.childNodes.length) {
20168                 var cn = node.childNodes[0];
20169                 node.removeChild(cn);
20170                 node.parentNode.insertBefore(cn, node);
20171             }
20172             node.parentNode.removeChild(node);
20173             this.iterateChildren(node, this.cleanWord);
20174             return;
20175         }
20176         // clean styles
20177         if (node.className.length) {
20178             
20179             var cn = node.className.split(/\W+/);
20180             var cna = [];
20181             Roo.each(cn, function(cls) {
20182                 if (cls.match(/Mso[a-zA-Z]+/)) {
20183                     return;
20184                 }
20185                 cna.push(cls);
20186             });
20187             node.className = cna.length ? cna.join(' ') : '';
20188             if (!cna.length) {
20189                 node.removeAttribute("class");
20190             }
20191         }
20192         
20193         if (node.hasAttribute("lang")) {
20194             node.removeAttribute("lang");
20195         }
20196         
20197         if (node.hasAttribute("style")) {
20198             
20199             var styles = node.getAttribute("style").split(";");
20200             var nstyle = [];
20201             Roo.each(styles, function(s) {
20202                 if (!s.match(/:/)) {
20203                     return;
20204                 }
20205                 var kv = s.split(":");
20206                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20207                     return;
20208                 }
20209                 // what ever is left... we allow.
20210                 nstyle.push(s);
20211             });
20212             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20213             if (!nstyle.length) {
20214                 node.removeAttribute('style');
20215             }
20216         }
20217         this.iterateChildren(node, this.cleanWord);
20218         
20219         
20220         
20221     },
20222     /**
20223      * iterateChildren of a Node, calling fn each time, using this as the scole..
20224      * @param {DomNode} node node to iterate children of.
20225      * @param {Function} fn method of this class to call on each item.
20226      */
20227     iterateChildren : function(node, fn)
20228     {
20229         if (!node.childNodes.length) {
20230                 return;
20231         }
20232         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20233            fn.call(this, node.childNodes[i])
20234         }
20235     },
20236     
20237     
20238     /**
20239      * cleanTableWidths.
20240      *
20241      * Quite often pasting from word etc.. results in tables with column and widths.
20242      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20243      *
20244      */
20245     cleanTableWidths : function(node)
20246     {
20247          
20248          
20249         if (!node) {
20250             this.cleanTableWidths(this.doc.body);
20251             return;
20252         }
20253         
20254         // ignore list...
20255         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20256             return; 
20257         }
20258         Roo.log(node.tagName);
20259         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20260             this.iterateChildren(node, this.cleanTableWidths);
20261             return;
20262         }
20263         if (node.hasAttribute('width')) {
20264             node.removeAttribute('width');
20265         }
20266         
20267          
20268         if (node.hasAttribute("style")) {
20269             // pretty basic...
20270             
20271             var styles = node.getAttribute("style").split(";");
20272             var nstyle = [];
20273             Roo.each(styles, function(s) {
20274                 if (!s.match(/:/)) {
20275                     return;
20276                 }
20277                 var kv = s.split(":");
20278                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20279                     return;
20280                 }
20281                 // what ever is left... we allow.
20282                 nstyle.push(s);
20283             });
20284             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20285             if (!nstyle.length) {
20286                 node.removeAttribute('style');
20287             }
20288         }
20289         
20290         this.iterateChildren(node, this.cleanTableWidths);
20291         
20292         
20293     },
20294     
20295     
20296     
20297     
20298     domToHTML : function(currentElement, depth, nopadtext) {
20299         
20300         depth = depth || 0;
20301         nopadtext = nopadtext || false;
20302     
20303         if (!currentElement) {
20304             return this.domToHTML(this.doc.body);
20305         }
20306         
20307         //Roo.log(currentElement);
20308         var j;
20309         var allText = false;
20310         var nodeName = currentElement.nodeName;
20311         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20312         
20313         if  (nodeName == '#text') {
20314             
20315             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20316         }
20317         
20318         
20319         var ret = '';
20320         if (nodeName != 'BODY') {
20321              
20322             var i = 0;
20323             // Prints the node tagName, such as <A>, <IMG>, etc
20324             if (tagName) {
20325                 var attr = [];
20326                 for(i = 0; i < currentElement.attributes.length;i++) {
20327                     // quoting?
20328                     var aname = currentElement.attributes.item(i).name;
20329                     if (!currentElement.attributes.item(i).value.length) {
20330                         continue;
20331                     }
20332                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20333                 }
20334                 
20335                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20336             } 
20337             else {
20338                 
20339                 // eack
20340             }
20341         } else {
20342             tagName = false;
20343         }
20344         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20345             return ret;
20346         }
20347         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20348             nopadtext = true;
20349         }
20350         
20351         
20352         // Traverse the tree
20353         i = 0;
20354         var currentElementChild = currentElement.childNodes.item(i);
20355         var allText = true;
20356         var innerHTML  = '';
20357         lastnode = '';
20358         while (currentElementChild) {
20359             // Formatting code (indent the tree so it looks nice on the screen)
20360             var nopad = nopadtext;
20361             if (lastnode == 'SPAN') {
20362                 nopad  = true;
20363             }
20364             // text
20365             if  (currentElementChild.nodeName == '#text') {
20366                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20367                 toadd = nopadtext ? toadd : toadd.trim();
20368                 if (!nopad && toadd.length > 80) {
20369                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20370                 }
20371                 innerHTML  += toadd;
20372                 
20373                 i++;
20374                 currentElementChild = currentElement.childNodes.item(i);
20375                 lastNode = '';
20376                 continue;
20377             }
20378             allText = false;
20379             
20380             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20381                 
20382             // Recursively traverse the tree structure of the child node
20383             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20384             lastnode = currentElementChild.nodeName;
20385             i++;
20386             currentElementChild=currentElement.childNodes.item(i);
20387         }
20388         
20389         ret += innerHTML;
20390         
20391         if (!allText) {
20392                 // The remaining code is mostly for formatting the tree
20393             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20394         }
20395         
20396         
20397         if (tagName) {
20398             ret+= "</"+tagName+">";
20399         }
20400         return ret;
20401         
20402     },
20403         
20404     applyBlacklists : function()
20405     {
20406         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20407         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20408         
20409         this.white = [];
20410         this.black = [];
20411         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20412             if (b.indexOf(tag) > -1) {
20413                 return;
20414             }
20415             this.white.push(tag);
20416             
20417         }, this);
20418         
20419         Roo.each(w, function(tag) {
20420             if (b.indexOf(tag) > -1) {
20421                 return;
20422             }
20423             if (this.white.indexOf(tag) > -1) {
20424                 return;
20425             }
20426             this.white.push(tag);
20427             
20428         }, this);
20429         
20430         
20431         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20432             if (w.indexOf(tag) > -1) {
20433                 return;
20434             }
20435             this.black.push(tag);
20436             
20437         }, this);
20438         
20439         Roo.each(b, function(tag) {
20440             if (w.indexOf(tag) > -1) {
20441                 return;
20442             }
20443             if (this.black.indexOf(tag) > -1) {
20444                 return;
20445             }
20446             this.black.push(tag);
20447             
20448         }, this);
20449         
20450         
20451         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20452         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20453         
20454         this.cwhite = [];
20455         this.cblack = [];
20456         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20457             if (b.indexOf(tag) > -1) {
20458                 return;
20459             }
20460             this.cwhite.push(tag);
20461             
20462         }, this);
20463         
20464         Roo.each(w, function(tag) {
20465             if (b.indexOf(tag) > -1) {
20466                 return;
20467             }
20468             if (this.cwhite.indexOf(tag) > -1) {
20469                 return;
20470             }
20471             this.cwhite.push(tag);
20472             
20473         }, this);
20474         
20475         
20476         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20477             if (w.indexOf(tag) > -1) {
20478                 return;
20479             }
20480             this.cblack.push(tag);
20481             
20482         }, this);
20483         
20484         Roo.each(b, function(tag) {
20485             if (w.indexOf(tag) > -1) {
20486                 return;
20487             }
20488             if (this.cblack.indexOf(tag) > -1) {
20489                 return;
20490             }
20491             this.cblack.push(tag);
20492             
20493         }, this);
20494     },
20495     
20496     setStylesheets : function(stylesheets)
20497     {
20498         if(typeof(stylesheets) == 'string'){
20499             Roo.get(this.iframe.contentDocument.head).createChild({
20500                 tag : 'link',
20501                 rel : 'stylesheet',
20502                 type : 'text/css',
20503                 href : stylesheets
20504             });
20505             
20506             return;
20507         }
20508         var _this = this;
20509      
20510         Roo.each(stylesheets, function(s) {
20511             if(!s.length){
20512                 return;
20513             }
20514             
20515             Roo.get(_this.iframe.contentDocument.head).createChild({
20516                 tag : 'link',
20517                 rel : 'stylesheet',
20518                 type : 'text/css',
20519                 href : s
20520             });
20521         });
20522
20523         
20524     },
20525     
20526     removeStylesheets : function()
20527     {
20528         var _this = this;
20529         
20530         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20531             s.remove();
20532         });
20533     }
20534     
20535     // hide stuff that is not compatible
20536     /**
20537      * @event blur
20538      * @hide
20539      */
20540     /**
20541      * @event change
20542      * @hide
20543      */
20544     /**
20545      * @event focus
20546      * @hide
20547      */
20548     /**
20549      * @event specialkey
20550      * @hide
20551      */
20552     /**
20553      * @cfg {String} fieldClass @hide
20554      */
20555     /**
20556      * @cfg {String} focusClass @hide
20557      */
20558     /**
20559      * @cfg {String} autoCreate @hide
20560      */
20561     /**
20562      * @cfg {String} inputType @hide
20563      */
20564     /**
20565      * @cfg {String} invalidClass @hide
20566      */
20567     /**
20568      * @cfg {String} invalidText @hide
20569      */
20570     /**
20571      * @cfg {String} msgFx @hide
20572      */
20573     /**
20574      * @cfg {String} validateOnBlur @hide
20575      */
20576 });
20577
20578 Roo.HtmlEditorCore.white = [
20579         'area', 'br', 'img', 'input', 'hr', 'wbr',
20580         
20581        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20582        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20583        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20584        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20585        'table',   'ul',         'xmp', 
20586        
20587        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20588       'thead',   'tr', 
20589      
20590       'dir', 'menu', 'ol', 'ul', 'dl',
20591        
20592       'embed',  'object'
20593 ];
20594
20595
20596 Roo.HtmlEditorCore.black = [
20597     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20598         'applet', // 
20599         'base',   'basefont', 'bgsound', 'blink',  'body', 
20600         'frame',  'frameset', 'head',    'html',   'ilayer', 
20601         'iframe', 'layer',  'link',     'meta',    'object',   
20602         'script', 'style' ,'title',  'xml' // clean later..
20603 ];
20604 Roo.HtmlEditorCore.clean = [
20605     'script', 'style', 'title', 'xml'
20606 ];
20607 Roo.HtmlEditorCore.remove = [
20608     'font'
20609 ];
20610 // attributes..
20611
20612 Roo.HtmlEditorCore.ablack = [
20613     'on'
20614 ];
20615     
20616 Roo.HtmlEditorCore.aclean = [ 
20617     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20618 ];
20619
20620 // protocols..
20621 Roo.HtmlEditorCore.pwhite= [
20622         'http',  'https',  'mailto'
20623 ];
20624
20625 // white listed style attributes.
20626 Roo.HtmlEditorCore.cwhite= [
20627       //  'text-align', /// default is to allow most things..
20628       
20629          
20630 //        'font-size'//??
20631 ];
20632
20633 // black listed style attributes.
20634 Roo.HtmlEditorCore.cblack= [
20635       //  'font-size' -- this can be set by the project 
20636 ];
20637
20638
20639 Roo.HtmlEditorCore.swapCodes   =[ 
20640     [    8211, "--" ], 
20641     [    8212, "--" ], 
20642     [    8216,  "'" ],  
20643     [    8217, "'" ],  
20644     [    8220, '"' ],  
20645     [    8221, '"' ],  
20646     [    8226, "*" ],  
20647     [    8230, "..." ]
20648 ]; 
20649
20650     /*
20651  * - LGPL
20652  *
20653  * HtmlEditor
20654  * 
20655  */
20656
20657 /**
20658  * @class Roo.bootstrap.HtmlEditor
20659  * @extends Roo.bootstrap.TextArea
20660  * Bootstrap HtmlEditor class
20661
20662  * @constructor
20663  * Create a new HtmlEditor
20664  * @param {Object} config The config object
20665  */
20666
20667 Roo.bootstrap.HtmlEditor = function(config){
20668     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20669     if (!this.toolbars) {
20670         this.toolbars = [];
20671     }
20672     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20673     this.addEvents({
20674             /**
20675              * @event initialize
20676              * Fires when the editor is fully initialized (including the iframe)
20677              * @param {HtmlEditor} this
20678              */
20679             initialize: true,
20680             /**
20681              * @event activate
20682              * Fires when the editor is first receives the focus. Any insertion must wait
20683              * until after this event.
20684              * @param {HtmlEditor} this
20685              */
20686             activate: true,
20687              /**
20688              * @event beforesync
20689              * Fires before the textarea is updated with content from the editor iframe. Return false
20690              * to cancel the sync.
20691              * @param {HtmlEditor} this
20692              * @param {String} html
20693              */
20694             beforesync: true,
20695              /**
20696              * @event beforepush
20697              * Fires before the iframe editor is updated with content from the textarea. Return false
20698              * to cancel the push.
20699              * @param {HtmlEditor} this
20700              * @param {String} html
20701              */
20702             beforepush: true,
20703              /**
20704              * @event sync
20705              * Fires when the textarea is updated with content from the editor iframe.
20706              * @param {HtmlEditor} this
20707              * @param {String} html
20708              */
20709             sync: true,
20710              /**
20711              * @event push
20712              * Fires when the iframe editor is updated with content from the textarea.
20713              * @param {HtmlEditor} this
20714              * @param {String} html
20715              */
20716             push: true,
20717              /**
20718              * @event editmodechange
20719              * Fires when the editor switches edit modes
20720              * @param {HtmlEditor} this
20721              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20722              */
20723             editmodechange: true,
20724             /**
20725              * @event editorevent
20726              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20727              * @param {HtmlEditor} this
20728              */
20729             editorevent: true,
20730             /**
20731              * @event firstfocus
20732              * Fires when on first focus - needed by toolbars..
20733              * @param {HtmlEditor} this
20734              */
20735             firstfocus: true,
20736             /**
20737              * @event autosave
20738              * Auto save the htmlEditor value as a file into Events
20739              * @param {HtmlEditor} this
20740              */
20741             autosave: true,
20742             /**
20743              * @event savedpreview
20744              * preview the saved version of htmlEditor
20745              * @param {HtmlEditor} this
20746              */
20747             savedpreview: true
20748         });
20749 };
20750
20751
20752 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20753     
20754     
20755       /**
20756      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20757      */
20758     toolbars : false,
20759    
20760      /**
20761      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20762      *                        Roo.resizable.
20763      */
20764     resizable : false,
20765      /**
20766      * @cfg {Number} height (in pixels)
20767      */   
20768     height: 300,
20769    /**
20770      * @cfg {Number} width (in pixels)
20771      */   
20772     width: false,
20773     
20774     /**
20775      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20776      * 
20777      */
20778     stylesheets: false,
20779     
20780     // id of frame..
20781     frameId: false,
20782     
20783     // private properties
20784     validationEvent : false,
20785     deferHeight: true,
20786     initialized : false,
20787     activated : false,
20788     
20789     onFocus : Roo.emptyFn,
20790     iframePad:3,
20791     hideMode:'offsets',
20792     
20793     
20794     tbContainer : false,
20795     
20796     toolbarContainer :function() {
20797         return this.wrap.select('.x-html-editor-tb',true).first();
20798     },
20799
20800     /**
20801      * Protected method that will not generally be called directly. It
20802      * is called when the editor creates its toolbar. Override this method if you need to
20803      * add custom toolbar buttons.
20804      * @param {HtmlEditor} editor
20805      */
20806     createToolbar : function(){
20807         
20808         Roo.log("create toolbars");
20809         
20810         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20811         this.toolbars[0].render(this.toolbarContainer());
20812         
20813         return;
20814         
20815 //        if (!editor.toolbars || !editor.toolbars.length) {
20816 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20817 //        }
20818 //        
20819 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20820 //            editor.toolbars[i] = Roo.factory(
20821 //                    typeof(editor.toolbars[i]) == 'string' ?
20822 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20823 //                Roo.bootstrap.HtmlEditor);
20824 //            editor.toolbars[i].init(editor);
20825 //        }
20826     },
20827
20828      
20829     // private
20830     onRender : function(ct, position)
20831     {
20832        // Roo.log("Call onRender: " + this.xtype);
20833         var _t = this;
20834         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20835       
20836         this.wrap = this.inputEl().wrap({
20837             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20838         });
20839         
20840         this.editorcore.onRender(ct, position);
20841          
20842         if (this.resizable) {
20843             this.resizeEl = new Roo.Resizable(this.wrap, {
20844                 pinned : true,
20845                 wrap: true,
20846                 dynamic : true,
20847                 minHeight : this.height,
20848                 height: this.height,
20849                 handles : this.resizable,
20850                 width: this.width,
20851                 listeners : {
20852                     resize : function(r, w, h) {
20853                         _t.onResize(w,h); // -something
20854                     }
20855                 }
20856             });
20857             
20858         }
20859         this.createToolbar(this);
20860        
20861         
20862         if(!this.width && this.resizable){
20863             this.setSize(this.wrap.getSize());
20864         }
20865         if (this.resizeEl) {
20866             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20867             // should trigger onReize..
20868         }
20869         
20870     },
20871
20872     // private
20873     onResize : function(w, h)
20874     {
20875         Roo.log('resize: ' +w + ',' + h );
20876         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20877         var ew = false;
20878         var eh = false;
20879         
20880         if(this.inputEl() ){
20881             if(typeof w == 'number'){
20882                 var aw = w - this.wrap.getFrameWidth('lr');
20883                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20884                 ew = aw;
20885             }
20886             if(typeof h == 'number'){
20887                  var tbh = -11;  // fixme it needs to tool bar size!
20888                 for (var i =0; i < this.toolbars.length;i++) {
20889                     // fixme - ask toolbars for heights?
20890                     tbh += this.toolbars[i].el.getHeight();
20891                     //if (this.toolbars[i].footer) {
20892                     //    tbh += this.toolbars[i].footer.el.getHeight();
20893                     //}
20894                 }
20895               
20896                 
20897                 
20898                 
20899                 
20900                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20901                 ah -= 5; // knock a few pixes off for look..
20902                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20903                 var eh = ah;
20904             }
20905         }
20906         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20907         this.editorcore.onResize(ew,eh);
20908         
20909     },
20910
20911     /**
20912      * Toggles the editor between standard and source edit mode.
20913      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20914      */
20915     toggleSourceEdit : function(sourceEditMode)
20916     {
20917         this.editorcore.toggleSourceEdit(sourceEditMode);
20918         
20919         if(this.editorcore.sourceEditMode){
20920             Roo.log('editor - showing textarea');
20921             
20922 //            Roo.log('in');
20923 //            Roo.log(this.syncValue());
20924             this.syncValue();
20925             this.inputEl().removeClass(['hide', 'x-hidden']);
20926             this.inputEl().dom.removeAttribute('tabIndex');
20927             this.inputEl().focus();
20928         }else{
20929             Roo.log('editor - hiding textarea');
20930 //            Roo.log('out')
20931 //            Roo.log(this.pushValue()); 
20932             this.pushValue();
20933             
20934             this.inputEl().addClass(['hide', 'x-hidden']);
20935             this.inputEl().dom.setAttribute('tabIndex', -1);
20936             //this.deferFocus();
20937         }
20938          
20939         if(this.resizable){
20940             this.setSize(this.wrap.getSize());
20941         }
20942         
20943         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20944     },
20945  
20946     // private (for BoxComponent)
20947     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20948
20949     // private (for BoxComponent)
20950     getResizeEl : function(){
20951         return this.wrap;
20952     },
20953
20954     // private (for BoxComponent)
20955     getPositionEl : function(){
20956         return this.wrap;
20957     },
20958
20959     // private
20960     initEvents : function(){
20961         this.originalValue = this.getValue();
20962     },
20963
20964 //    /**
20965 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20966 //     * @method
20967 //     */
20968 //    markInvalid : Roo.emptyFn,
20969 //    /**
20970 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20971 //     * @method
20972 //     */
20973 //    clearInvalid : Roo.emptyFn,
20974
20975     setValue : function(v){
20976         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20977         this.editorcore.pushValue();
20978     },
20979
20980      
20981     // private
20982     deferFocus : function(){
20983         this.focus.defer(10, this);
20984     },
20985
20986     // doc'ed in Field
20987     focus : function(){
20988         this.editorcore.focus();
20989         
20990     },
20991       
20992
20993     // private
20994     onDestroy : function(){
20995         
20996         
20997         
20998         if(this.rendered){
20999             
21000             for (var i =0; i < this.toolbars.length;i++) {
21001                 // fixme - ask toolbars for heights?
21002                 this.toolbars[i].onDestroy();
21003             }
21004             
21005             this.wrap.dom.innerHTML = '';
21006             this.wrap.remove();
21007         }
21008     },
21009
21010     // private
21011     onFirstFocus : function(){
21012         //Roo.log("onFirstFocus");
21013         this.editorcore.onFirstFocus();
21014          for (var i =0; i < this.toolbars.length;i++) {
21015             this.toolbars[i].onFirstFocus();
21016         }
21017         
21018     },
21019     
21020     // private
21021     syncValue : function()
21022     {   
21023         this.editorcore.syncValue();
21024     },
21025     
21026     pushValue : function()
21027     {   
21028         this.editorcore.pushValue();
21029     }
21030      
21031     
21032     // hide stuff that is not compatible
21033     /**
21034      * @event blur
21035      * @hide
21036      */
21037     /**
21038      * @event change
21039      * @hide
21040      */
21041     /**
21042      * @event focus
21043      * @hide
21044      */
21045     /**
21046      * @event specialkey
21047      * @hide
21048      */
21049     /**
21050      * @cfg {String} fieldClass @hide
21051      */
21052     /**
21053      * @cfg {String} focusClass @hide
21054      */
21055     /**
21056      * @cfg {String} autoCreate @hide
21057      */
21058     /**
21059      * @cfg {String} inputType @hide
21060      */
21061     /**
21062      * @cfg {String} invalidClass @hide
21063      */
21064     /**
21065      * @cfg {String} invalidText @hide
21066      */
21067     /**
21068      * @cfg {String} msgFx @hide
21069      */
21070     /**
21071      * @cfg {String} validateOnBlur @hide
21072      */
21073 });
21074  
21075     
21076    
21077    
21078    
21079       
21080 Roo.namespace('Roo.bootstrap.htmleditor');
21081 /**
21082  * @class Roo.bootstrap.HtmlEditorToolbar1
21083  * Basic Toolbar
21084  * 
21085  * Usage:
21086  *
21087  new Roo.bootstrap.HtmlEditor({
21088     ....
21089     toolbars : [
21090         new Roo.bootstrap.HtmlEditorToolbar1({
21091             disable : { fonts: 1 , format: 1, ..., ... , ...],
21092             btns : [ .... ]
21093         })
21094     }
21095      
21096  * 
21097  * @cfg {Object} disable List of elements to disable..
21098  * @cfg {Array} btns List of additional buttons.
21099  * 
21100  * 
21101  * NEEDS Extra CSS? 
21102  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21103  */
21104  
21105 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21106 {
21107     
21108     Roo.apply(this, config);
21109     
21110     // default disabled, based on 'good practice'..
21111     this.disable = this.disable || {};
21112     Roo.applyIf(this.disable, {
21113         fontSize : true,
21114         colors : true,
21115         specialElements : true
21116     });
21117     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21118     
21119     this.editor = config.editor;
21120     this.editorcore = config.editor.editorcore;
21121     
21122     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21123     
21124     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21125     // dont call parent... till later.
21126 }
21127 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21128      
21129     bar : true,
21130     
21131     editor : false,
21132     editorcore : false,
21133     
21134     
21135     formats : [
21136         "p" ,  
21137         "h1","h2","h3","h4","h5","h6", 
21138         "pre", "code", 
21139         "abbr", "acronym", "address", "cite", "samp", "var",
21140         'div','span'
21141     ],
21142     
21143     onRender : function(ct, position)
21144     {
21145        // Roo.log("Call onRender: " + this.xtype);
21146         
21147        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21148        Roo.log(this.el);
21149        this.el.dom.style.marginBottom = '0';
21150        var _this = this;
21151        var editorcore = this.editorcore;
21152        var editor= this.editor;
21153        
21154        var children = [];
21155        var btn = function(id,cmd , toggle, handler){
21156        
21157             var  event = toggle ? 'toggle' : 'click';
21158        
21159             var a = {
21160                 size : 'sm',
21161                 xtype: 'Button',
21162                 xns: Roo.bootstrap,
21163                 glyphicon : id,
21164                 cmd : id || cmd,
21165                 enableToggle:toggle !== false,
21166                 //html : 'submit'
21167                 pressed : toggle ? false : null,
21168                 listeners : {}
21169             };
21170             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21171                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21172             };
21173             children.push(a);
21174             return a;
21175        }
21176         
21177         var style = {
21178                 xtype: 'Button',
21179                 size : 'sm',
21180                 xns: Roo.bootstrap,
21181                 glyphicon : 'font',
21182                 //html : 'submit'
21183                 menu : {
21184                     xtype: 'Menu',
21185                     xns: Roo.bootstrap,
21186                     items:  []
21187                 }
21188         };
21189         Roo.each(this.formats, function(f) {
21190             style.menu.items.push({
21191                 xtype :'MenuItem',
21192                 xns: Roo.bootstrap,
21193                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21194                 tagname : f,
21195                 listeners : {
21196                     click : function()
21197                     {
21198                         editorcore.insertTag(this.tagname);
21199                         editor.focus();
21200                     }
21201                 }
21202                 
21203             });
21204         });
21205          children.push(style);   
21206             
21207             
21208         btn('bold',false,true);
21209         btn('italic',false,true);
21210         btn('align-left', 'justifyleft',true);
21211         btn('align-center', 'justifycenter',true);
21212         btn('align-right' , 'justifyright',true);
21213         btn('link', false, false, function(btn) {
21214             //Roo.log("create link?");
21215             var url = prompt(this.createLinkText, this.defaultLinkValue);
21216             if(url && url != 'http:/'+'/'){
21217                 this.editorcore.relayCmd('createlink', url);
21218             }
21219         }),
21220         btn('list','insertunorderedlist',true);
21221         btn('pencil', false,true, function(btn){
21222                 Roo.log(this);
21223                 
21224                 this.toggleSourceEdit(btn.pressed);
21225         });
21226         /*
21227         var cog = {
21228                 xtype: 'Button',
21229                 size : 'sm',
21230                 xns: Roo.bootstrap,
21231                 glyphicon : 'cog',
21232                 //html : 'submit'
21233                 menu : {
21234                     xtype: 'Menu',
21235                     xns: Roo.bootstrap,
21236                     items:  []
21237                 }
21238         };
21239         
21240         cog.menu.items.push({
21241             xtype :'MenuItem',
21242             xns: Roo.bootstrap,
21243             html : Clean styles,
21244             tagname : f,
21245             listeners : {
21246                 click : function()
21247                 {
21248                     editorcore.insertTag(this.tagname);
21249                     editor.focus();
21250                 }
21251             }
21252             
21253         });
21254        */
21255         
21256          
21257        this.xtype = 'NavSimplebar';
21258         
21259         for(var i=0;i< children.length;i++) {
21260             
21261             this.buttons.add(this.addxtypeChild(children[i]));
21262             
21263         }
21264         
21265         editor.on('editorevent', this.updateToolbar, this);
21266     },
21267     onBtnClick : function(id)
21268     {
21269        this.editorcore.relayCmd(id);
21270        this.editorcore.focus();
21271     },
21272     
21273     /**
21274      * Protected method that will not generally be called directly. It triggers
21275      * a toolbar update by reading the markup state of the current selection in the editor.
21276      */
21277     updateToolbar: function(){
21278
21279         if(!this.editorcore.activated){
21280             this.editor.onFirstFocus(); // is this neeed?
21281             return;
21282         }
21283
21284         var btns = this.buttons; 
21285         var doc = this.editorcore.doc;
21286         btns.get('bold').setActive(doc.queryCommandState('bold'));
21287         btns.get('italic').setActive(doc.queryCommandState('italic'));
21288         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21289         
21290         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21291         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21292         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21293         
21294         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21295         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21296          /*
21297         
21298         var ans = this.editorcore.getAllAncestors();
21299         if (this.formatCombo) {
21300             
21301             
21302             var store = this.formatCombo.store;
21303             this.formatCombo.setValue("");
21304             for (var i =0; i < ans.length;i++) {
21305                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21306                     // select it..
21307                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21308                     break;
21309                 }
21310             }
21311         }
21312         
21313         
21314         
21315         // hides menus... - so this cant be on a menu...
21316         Roo.bootstrap.MenuMgr.hideAll();
21317         */
21318         Roo.bootstrap.MenuMgr.hideAll();
21319         //this.editorsyncValue();
21320     },
21321     onFirstFocus: function() {
21322         this.buttons.each(function(item){
21323            item.enable();
21324         });
21325     },
21326     toggleSourceEdit : function(sourceEditMode){
21327         
21328           
21329         if(sourceEditMode){
21330             Roo.log("disabling buttons");
21331            this.buttons.each( function(item){
21332                 if(item.cmd != 'pencil'){
21333                     item.disable();
21334                 }
21335             });
21336           
21337         }else{
21338             Roo.log("enabling buttons");
21339             if(this.editorcore.initialized){
21340                 this.buttons.each( function(item){
21341                     item.enable();
21342                 });
21343             }
21344             
21345         }
21346         Roo.log("calling toggole on editor");
21347         // tell the editor that it's been pressed..
21348         this.editor.toggleSourceEdit(sourceEditMode);
21349        
21350     }
21351 });
21352
21353
21354
21355
21356
21357 /**
21358  * @class Roo.bootstrap.Table.AbstractSelectionModel
21359  * @extends Roo.util.Observable
21360  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21361  * implemented by descendant classes.  This class should not be directly instantiated.
21362  * @constructor
21363  */
21364 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21365     this.locked = false;
21366     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21367 };
21368
21369
21370 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21371     /** @ignore Called by the grid automatically. Do not call directly. */
21372     init : function(grid){
21373         this.grid = grid;
21374         this.initEvents();
21375     },
21376
21377     /**
21378      * Locks the selections.
21379      */
21380     lock : function(){
21381         this.locked = true;
21382     },
21383
21384     /**
21385      * Unlocks the selections.
21386      */
21387     unlock : function(){
21388         this.locked = false;
21389     },
21390
21391     /**
21392      * Returns true if the selections are locked.
21393      * @return {Boolean}
21394      */
21395     isLocked : function(){
21396         return this.locked;
21397     }
21398 });
21399 /**
21400  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21401  * @class Roo.bootstrap.Table.RowSelectionModel
21402  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21403  * It supports multiple selections and keyboard selection/navigation. 
21404  * @constructor
21405  * @param {Object} config
21406  */
21407
21408 Roo.bootstrap.Table.RowSelectionModel = function(config){
21409     Roo.apply(this, config);
21410     this.selections = new Roo.util.MixedCollection(false, function(o){
21411         return o.id;
21412     });
21413
21414     this.last = false;
21415     this.lastActive = false;
21416
21417     this.addEvents({
21418         /**
21419              * @event selectionchange
21420              * Fires when the selection changes
21421              * @param {SelectionModel} this
21422              */
21423             "selectionchange" : true,
21424         /**
21425              * @event afterselectionchange
21426              * Fires after the selection changes (eg. by key press or clicking)
21427              * @param {SelectionModel} this
21428              */
21429             "afterselectionchange" : true,
21430         /**
21431              * @event beforerowselect
21432              * Fires when a row is selected being selected, return false to cancel.
21433              * @param {SelectionModel} this
21434              * @param {Number} rowIndex The selected index
21435              * @param {Boolean} keepExisting False if other selections will be cleared
21436              */
21437             "beforerowselect" : true,
21438         /**
21439              * @event rowselect
21440              * Fires when a row is selected.
21441              * @param {SelectionModel} this
21442              * @param {Number} rowIndex The selected index
21443              * @param {Roo.data.Record} r The record
21444              */
21445             "rowselect" : true,
21446         /**
21447              * @event rowdeselect
21448              * Fires when a row is deselected.
21449              * @param {SelectionModel} this
21450              * @param {Number} rowIndex The selected index
21451              */
21452         "rowdeselect" : true
21453     });
21454     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21455     this.locked = false;
21456 };
21457
21458 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21459     /**
21460      * @cfg {Boolean} singleSelect
21461      * True to allow selection of only one row at a time (defaults to false)
21462      */
21463     singleSelect : false,
21464
21465     // private
21466     initEvents : function(){
21467
21468         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21469             this.grid.on("mousedown", this.handleMouseDown, this);
21470         }else{ // allow click to work like normal
21471             this.grid.on("rowclick", this.handleDragableRowClick, this);
21472         }
21473
21474         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21475             "up" : function(e){
21476                 if(!e.shiftKey){
21477                     this.selectPrevious(e.shiftKey);
21478                 }else if(this.last !== false && this.lastActive !== false){
21479                     var last = this.last;
21480                     this.selectRange(this.last,  this.lastActive-1);
21481                     this.grid.getView().focusRow(this.lastActive);
21482                     if(last !== false){
21483                         this.last = last;
21484                     }
21485                 }else{
21486                     this.selectFirstRow();
21487                 }
21488                 this.fireEvent("afterselectionchange", this);
21489             },
21490             "down" : function(e){
21491                 if(!e.shiftKey){
21492                     this.selectNext(e.shiftKey);
21493                 }else if(this.last !== false && this.lastActive !== false){
21494                     var last = this.last;
21495                     this.selectRange(this.last,  this.lastActive+1);
21496                     this.grid.getView().focusRow(this.lastActive);
21497                     if(last !== false){
21498                         this.last = last;
21499                     }
21500                 }else{
21501                     this.selectFirstRow();
21502                 }
21503                 this.fireEvent("afterselectionchange", this);
21504             },
21505             scope: this
21506         });
21507
21508         var view = this.grid.view;
21509         view.on("refresh", this.onRefresh, this);
21510         view.on("rowupdated", this.onRowUpdated, this);
21511         view.on("rowremoved", this.onRemove, this);
21512     },
21513
21514     // private
21515     onRefresh : function(){
21516         var ds = this.grid.dataSource, i, v = this.grid.view;
21517         var s = this.selections;
21518         s.each(function(r){
21519             if((i = ds.indexOfId(r.id)) != -1){
21520                 v.onRowSelect(i);
21521             }else{
21522                 s.remove(r);
21523             }
21524         });
21525     },
21526
21527     // private
21528     onRemove : function(v, index, r){
21529         this.selections.remove(r);
21530     },
21531
21532     // private
21533     onRowUpdated : function(v, index, r){
21534         if(this.isSelected(r)){
21535             v.onRowSelect(index);
21536         }
21537     },
21538
21539     /**
21540      * Select records.
21541      * @param {Array} records The records to select
21542      * @param {Boolean} keepExisting (optional) True to keep existing selections
21543      */
21544     selectRecords : function(records, keepExisting){
21545         if(!keepExisting){
21546             this.clearSelections();
21547         }
21548         var ds = this.grid.dataSource;
21549         for(var i = 0, len = records.length; i < len; i++){
21550             this.selectRow(ds.indexOf(records[i]), true);
21551         }
21552     },
21553
21554     /**
21555      * Gets the number of selected rows.
21556      * @return {Number}
21557      */
21558     getCount : function(){
21559         return this.selections.length;
21560     },
21561
21562     /**
21563      * Selects the first row in the grid.
21564      */
21565     selectFirstRow : function(){
21566         this.selectRow(0);
21567     },
21568
21569     /**
21570      * Select the last row.
21571      * @param {Boolean} keepExisting (optional) True to keep existing selections
21572      */
21573     selectLastRow : function(keepExisting){
21574         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21575     },
21576
21577     /**
21578      * Selects the row immediately following the last selected row.
21579      * @param {Boolean} keepExisting (optional) True to keep existing selections
21580      */
21581     selectNext : function(keepExisting){
21582         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21583             this.selectRow(this.last+1, keepExisting);
21584             this.grid.getView().focusRow(this.last);
21585         }
21586     },
21587
21588     /**
21589      * Selects the row that precedes the last selected row.
21590      * @param {Boolean} keepExisting (optional) True to keep existing selections
21591      */
21592     selectPrevious : function(keepExisting){
21593         if(this.last){
21594             this.selectRow(this.last-1, keepExisting);
21595             this.grid.getView().focusRow(this.last);
21596         }
21597     },
21598
21599     /**
21600      * Returns the selected records
21601      * @return {Array} Array of selected records
21602      */
21603     getSelections : function(){
21604         return [].concat(this.selections.items);
21605     },
21606
21607     /**
21608      * Returns the first selected record.
21609      * @return {Record}
21610      */
21611     getSelected : function(){
21612         return this.selections.itemAt(0);
21613     },
21614
21615
21616     /**
21617      * Clears all selections.
21618      */
21619     clearSelections : function(fast){
21620         if(this.locked) return;
21621         if(fast !== true){
21622             var ds = this.grid.dataSource;
21623             var s = this.selections;
21624             s.each(function(r){
21625                 this.deselectRow(ds.indexOfId(r.id));
21626             }, this);
21627             s.clear();
21628         }else{
21629             this.selections.clear();
21630         }
21631         this.last = false;
21632     },
21633
21634
21635     /**
21636      * Selects all rows.
21637      */
21638     selectAll : function(){
21639         if(this.locked) return;
21640         this.selections.clear();
21641         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21642             this.selectRow(i, true);
21643         }
21644     },
21645
21646     /**
21647      * Returns True if there is a selection.
21648      * @return {Boolean}
21649      */
21650     hasSelection : function(){
21651         return this.selections.length > 0;
21652     },
21653
21654     /**
21655      * Returns True if the specified row is selected.
21656      * @param {Number/Record} record The record or index of the record to check
21657      * @return {Boolean}
21658      */
21659     isSelected : function(index){
21660         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21661         return (r && this.selections.key(r.id) ? true : false);
21662     },
21663
21664     /**
21665      * Returns True if the specified record id is selected.
21666      * @param {String} id The id of record to check
21667      * @return {Boolean}
21668      */
21669     isIdSelected : function(id){
21670         return (this.selections.key(id) ? true : false);
21671     },
21672
21673     // private
21674     handleMouseDown : function(e, t){
21675         var view = this.grid.getView(), rowIndex;
21676         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21677             return;
21678         };
21679         if(e.shiftKey && this.last !== false){
21680             var last = this.last;
21681             this.selectRange(last, rowIndex, e.ctrlKey);
21682             this.last = last; // reset the last
21683             view.focusRow(rowIndex);
21684         }else{
21685             var isSelected = this.isSelected(rowIndex);
21686             if(e.button !== 0 && isSelected){
21687                 view.focusRow(rowIndex);
21688             }else if(e.ctrlKey && isSelected){
21689                 this.deselectRow(rowIndex);
21690             }else if(!isSelected){
21691                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21692                 view.focusRow(rowIndex);
21693             }
21694         }
21695         this.fireEvent("afterselectionchange", this);
21696     },
21697     // private
21698     handleDragableRowClick :  function(grid, rowIndex, e) 
21699     {
21700         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21701             this.selectRow(rowIndex, false);
21702             grid.view.focusRow(rowIndex);
21703              this.fireEvent("afterselectionchange", this);
21704         }
21705     },
21706     
21707     /**
21708      * Selects multiple rows.
21709      * @param {Array} rows Array of the indexes of the row to select
21710      * @param {Boolean} keepExisting (optional) True to keep existing selections
21711      */
21712     selectRows : function(rows, keepExisting){
21713         if(!keepExisting){
21714             this.clearSelections();
21715         }
21716         for(var i = 0, len = rows.length; i < len; i++){
21717             this.selectRow(rows[i], true);
21718         }
21719     },
21720
21721     /**
21722      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21723      * @param {Number} startRow The index of the first row in the range
21724      * @param {Number} endRow The index of the last row in the range
21725      * @param {Boolean} keepExisting (optional) True to retain existing selections
21726      */
21727     selectRange : function(startRow, endRow, keepExisting){
21728         if(this.locked) return;
21729         if(!keepExisting){
21730             this.clearSelections();
21731         }
21732         if(startRow <= endRow){
21733             for(var i = startRow; i <= endRow; i++){
21734                 this.selectRow(i, true);
21735             }
21736         }else{
21737             for(var i = startRow; i >= endRow; i--){
21738                 this.selectRow(i, true);
21739             }
21740         }
21741     },
21742
21743     /**
21744      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21745      * @param {Number} startRow The index of the first row in the range
21746      * @param {Number} endRow The index of the last row in the range
21747      */
21748     deselectRange : function(startRow, endRow, preventViewNotify){
21749         if(this.locked) return;
21750         for(var i = startRow; i <= endRow; i++){
21751             this.deselectRow(i, preventViewNotify);
21752         }
21753     },
21754
21755     /**
21756      * Selects a row.
21757      * @param {Number} row The index of the row to select
21758      * @param {Boolean} keepExisting (optional) True to keep existing selections
21759      */
21760     selectRow : function(index, keepExisting, preventViewNotify){
21761         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21762         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21763             if(!keepExisting || this.singleSelect){
21764                 this.clearSelections();
21765             }
21766             var r = this.grid.dataSource.getAt(index);
21767             this.selections.add(r);
21768             this.last = this.lastActive = index;
21769             if(!preventViewNotify){
21770                 this.grid.getView().onRowSelect(index);
21771             }
21772             this.fireEvent("rowselect", this, index, r);
21773             this.fireEvent("selectionchange", this);
21774         }
21775     },
21776
21777     /**
21778      * Deselects a row.
21779      * @param {Number} row The index of the row to deselect
21780      */
21781     deselectRow : function(index, preventViewNotify){
21782         if(this.locked) return;
21783         if(this.last == index){
21784             this.last = false;
21785         }
21786         if(this.lastActive == index){
21787             this.lastActive = false;
21788         }
21789         var r = this.grid.dataSource.getAt(index);
21790         this.selections.remove(r);
21791         if(!preventViewNotify){
21792             this.grid.getView().onRowDeselect(index);
21793         }
21794         this.fireEvent("rowdeselect", this, index);
21795         this.fireEvent("selectionchange", this);
21796     },
21797
21798     // private
21799     restoreLast : function(){
21800         if(this._last){
21801             this.last = this._last;
21802         }
21803     },
21804
21805     // private
21806     acceptsNav : function(row, col, cm){
21807         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21808     },
21809
21810     // private
21811     onEditorKey : function(field, e){
21812         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21813         if(k == e.TAB){
21814             e.stopEvent();
21815             ed.completeEdit();
21816             if(e.shiftKey){
21817                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21818             }else{
21819                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21820             }
21821         }else if(k == e.ENTER && !e.ctrlKey){
21822             e.stopEvent();
21823             ed.completeEdit();
21824             if(e.shiftKey){
21825                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21826             }else{
21827                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21828             }
21829         }else if(k == e.ESC){
21830             ed.cancelEdit();
21831         }
21832         if(newCell){
21833             g.startEditing(newCell[0], newCell[1]);
21834         }
21835     }
21836 });/*
21837  * Based on:
21838  * Ext JS Library 1.1.1
21839  * Copyright(c) 2006-2007, Ext JS, LLC.
21840  *
21841  * Originally Released Under LGPL - original licence link has changed is not relivant.
21842  *
21843  * Fork - LGPL
21844  * <script type="text/javascript">
21845  */
21846  
21847 /**
21848  * @class Roo.bootstrap.PagingToolbar
21849  * @extends Roo.bootstrap.NavSimplebar
21850  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21851  * @constructor
21852  * Create a new PagingToolbar
21853  * @param {Object} config The config object
21854  * @param {Roo.data.Store} store
21855  */
21856 Roo.bootstrap.PagingToolbar = function(config)
21857 {
21858     // old args format still supported... - xtype is prefered..
21859         // created from xtype...
21860     
21861     this.ds = config.dataSource;
21862     
21863     if (config.store && !this.ds) {
21864         this.store= Roo.factory(config.store, Roo.data);
21865         this.ds = this.store;
21866         this.ds.xmodule = this.xmodule || false;
21867     }
21868     
21869     this.toolbarItems = [];
21870     if (config.items) {
21871         this.toolbarItems = config.items;
21872     }
21873     
21874     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21875     
21876     this.cursor = 0;
21877     
21878     if (this.ds) { 
21879         this.bind(this.ds);
21880     }
21881     
21882     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21883     
21884 };
21885
21886 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21887     /**
21888      * @cfg {Roo.data.Store} dataSource
21889      * The underlying data store providing the paged data
21890      */
21891     /**
21892      * @cfg {String/HTMLElement/Element} container
21893      * container The id or element that will contain the toolbar
21894      */
21895     /**
21896      * @cfg {Boolean} displayInfo
21897      * True to display the displayMsg (defaults to false)
21898      */
21899     /**
21900      * @cfg {Number} pageSize
21901      * The number of records to display per page (defaults to 20)
21902      */
21903     pageSize: 20,
21904     /**
21905      * @cfg {String} displayMsg
21906      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21907      */
21908     displayMsg : 'Displaying {0} - {1} of {2}',
21909     /**
21910      * @cfg {String} emptyMsg
21911      * The message to display when no records are found (defaults to "No data to display")
21912      */
21913     emptyMsg : 'No data to display',
21914     /**
21915      * Customizable piece of the default paging text (defaults to "Page")
21916      * @type String
21917      */
21918     beforePageText : "Page",
21919     /**
21920      * Customizable piece of the default paging text (defaults to "of %0")
21921      * @type String
21922      */
21923     afterPageText : "of {0}",
21924     /**
21925      * Customizable piece of the default paging text (defaults to "First Page")
21926      * @type String
21927      */
21928     firstText : "First Page",
21929     /**
21930      * Customizable piece of the default paging text (defaults to "Previous Page")
21931      * @type String
21932      */
21933     prevText : "Previous Page",
21934     /**
21935      * Customizable piece of the default paging text (defaults to "Next Page")
21936      * @type String
21937      */
21938     nextText : "Next Page",
21939     /**
21940      * Customizable piece of the default paging text (defaults to "Last Page")
21941      * @type String
21942      */
21943     lastText : "Last Page",
21944     /**
21945      * Customizable piece of the default paging text (defaults to "Refresh")
21946      * @type String
21947      */
21948     refreshText : "Refresh",
21949
21950     buttons : false,
21951     // private
21952     onRender : function(ct, position) 
21953     {
21954         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21955         this.navgroup.parentId = this.id;
21956         this.navgroup.onRender(this.el, null);
21957         // add the buttons to the navgroup
21958         
21959         if(this.displayInfo){
21960             Roo.log(this.el.select('ul.navbar-nav',true).first());
21961             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21962             this.displayEl = this.el.select('.x-paging-info', true).first();
21963 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21964 //            this.displayEl = navel.el.select('span',true).first();
21965         }
21966         
21967         var _this = this;
21968         
21969         if(this.buttons){
21970             Roo.each(_this.buttons, function(e){ // this might need to use render????
21971                Roo.factory(e).onRender(_this.el, null);
21972             });
21973         }
21974             
21975         Roo.each(_this.toolbarItems, function(e) {
21976             _this.navgroup.addItem(e);
21977         });
21978         
21979         
21980         this.first = this.navgroup.addItem({
21981             tooltip: this.firstText,
21982             cls: "prev",
21983             icon : 'fa fa-backward',
21984             disabled: true,
21985             preventDefault: true,
21986             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21987         });
21988         
21989         this.prev =  this.navgroup.addItem({
21990             tooltip: this.prevText,
21991             cls: "prev",
21992             icon : 'fa fa-step-backward',
21993             disabled: true,
21994             preventDefault: true,
21995             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21996         });
21997     //this.addSeparator();
21998         
21999         
22000         var field = this.navgroup.addItem( {
22001             tagtype : 'span',
22002             cls : 'x-paging-position',
22003             
22004             html : this.beforePageText  +
22005                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22006                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22007          } ); //?? escaped?
22008         
22009         this.field = field.el.select('input', true).first();
22010         this.field.on("keydown", this.onPagingKeydown, this);
22011         this.field.on("focus", function(){this.dom.select();});
22012     
22013     
22014         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22015         //this.field.setHeight(18);
22016         //this.addSeparator();
22017         this.next = this.navgroup.addItem({
22018             tooltip: this.nextText,
22019             cls: "next",
22020             html : ' <i class="fa fa-step-forward">',
22021             disabled: true,
22022             preventDefault: true,
22023             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22024         });
22025         this.last = this.navgroup.addItem({
22026             tooltip: this.lastText,
22027             icon : 'fa fa-forward',
22028             cls: "next",
22029             disabled: true,
22030             preventDefault: true,
22031             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22032         });
22033     //this.addSeparator();
22034         this.loading = this.navgroup.addItem({
22035             tooltip: this.refreshText,
22036             icon: 'fa fa-refresh',
22037             preventDefault: true,
22038             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22039         });
22040         
22041     },
22042
22043     // private
22044     updateInfo : function(){
22045         if(this.displayEl){
22046             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22047             var msg = count == 0 ?
22048                 this.emptyMsg :
22049                 String.format(
22050                     this.displayMsg,
22051                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22052                 );
22053             this.displayEl.update(msg);
22054         }
22055     },
22056
22057     // private
22058     onLoad : function(ds, r, o){
22059        this.cursor = o.params ? o.params.start : 0;
22060        var d = this.getPageData(),
22061             ap = d.activePage,
22062             ps = d.pages;
22063         
22064        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22065        this.field.dom.value = ap;
22066        this.first.setDisabled(ap == 1);
22067        this.prev.setDisabled(ap == 1);
22068        this.next.setDisabled(ap == ps);
22069        this.last.setDisabled(ap == ps);
22070        this.loading.enable();
22071        this.updateInfo();
22072     },
22073
22074     // private
22075     getPageData : function(){
22076         var total = this.ds.getTotalCount();
22077         return {
22078             total : total,
22079             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22080             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22081         };
22082     },
22083
22084     // private
22085     onLoadError : function(){
22086         this.loading.enable();
22087     },
22088
22089     // private
22090     onPagingKeydown : function(e){
22091         var k = e.getKey();
22092         var d = this.getPageData();
22093         if(k == e.RETURN){
22094             var v = this.field.dom.value, pageNum;
22095             if(!v || isNaN(pageNum = parseInt(v, 10))){
22096                 this.field.dom.value = d.activePage;
22097                 return;
22098             }
22099             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22100             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22101             e.stopEvent();
22102         }
22103         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))
22104         {
22105           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22106           this.field.dom.value = pageNum;
22107           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22108           e.stopEvent();
22109         }
22110         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22111         {
22112           var v = this.field.dom.value, pageNum; 
22113           var increment = (e.shiftKey) ? 10 : 1;
22114           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22115             increment *= -1;
22116           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22117             this.field.dom.value = d.activePage;
22118             return;
22119           }
22120           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22121           {
22122             this.field.dom.value = parseInt(v, 10) + increment;
22123             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22124             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22125           }
22126           e.stopEvent();
22127         }
22128     },
22129
22130     // private
22131     beforeLoad : function(){
22132         if(this.loading){
22133             this.loading.disable();
22134         }
22135     },
22136
22137     // private
22138     onClick : function(which){
22139         
22140         var ds = this.ds;
22141         if (!ds) {
22142             return;
22143         }
22144         
22145         switch(which){
22146             case "first":
22147                 ds.load({params:{start: 0, limit: this.pageSize}});
22148             break;
22149             case "prev":
22150                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22151             break;
22152             case "next":
22153                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22154             break;
22155             case "last":
22156                 var total = ds.getTotalCount();
22157                 var extra = total % this.pageSize;
22158                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22159                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22160             break;
22161             case "refresh":
22162                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22163             break;
22164         }
22165     },
22166
22167     /**
22168      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22169      * @param {Roo.data.Store} store The data store to unbind
22170      */
22171     unbind : function(ds){
22172         ds.un("beforeload", this.beforeLoad, this);
22173         ds.un("load", this.onLoad, this);
22174         ds.un("loadexception", this.onLoadError, this);
22175         ds.un("remove", this.updateInfo, this);
22176         ds.un("add", this.updateInfo, this);
22177         this.ds = undefined;
22178     },
22179
22180     /**
22181      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22182      * @param {Roo.data.Store} store The data store to bind
22183      */
22184     bind : function(ds){
22185         ds.on("beforeload", this.beforeLoad, this);
22186         ds.on("load", this.onLoad, this);
22187         ds.on("loadexception", this.onLoadError, this);
22188         ds.on("remove", this.updateInfo, this);
22189         ds.on("add", this.updateInfo, this);
22190         this.ds = ds;
22191     }
22192 });/*
22193  * - LGPL
22194  *
22195  * element
22196  * 
22197  */
22198
22199 /**
22200  * @class Roo.bootstrap.MessageBar
22201  * @extends Roo.bootstrap.Component
22202  * Bootstrap MessageBar class
22203  * @cfg {String} html contents of the MessageBar
22204  * @cfg {String} weight (info | success | warning | danger) default info
22205  * @cfg {String} beforeClass insert the bar before the given class
22206  * @cfg {Boolean} closable (true | false) default false
22207  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22208  * 
22209  * @constructor
22210  * Create a new Element
22211  * @param {Object} config The config object
22212  */
22213
22214 Roo.bootstrap.MessageBar = function(config){
22215     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22216 };
22217
22218 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22219     
22220     html: '',
22221     weight: 'info',
22222     closable: false,
22223     fixed: false,
22224     beforeClass: 'bootstrap-sticky-wrap',
22225     
22226     getAutoCreate : function(){
22227         
22228         var cfg = {
22229             tag: 'div',
22230             cls: 'alert alert-dismissable alert-' + this.weight,
22231             cn: [
22232                 {
22233                     tag: 'span',
22234                     cls: 'message',
22235                     html: this.html || ''
22236                 }
22237             ]
22238         }
22239         
22240         if(this.fixed){
22241             cfg.cls += ' alert-messages-fixed';
22242         }
22243         
22244         if(this.closable){
22245             cfg.cn.push({
22246                 tag: 'button',
22247                 cls: 'close',
22248                 html: 'x'
22249             });
22250         }
22251         
22252         return cfg;
22253     },
22254     
22255     onRender : function(ct, position)
22256     {
22257         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22258         
22259         if(!this.el){
22260             var cfg = Roo.apply({},  this.getAutoCreate());
22261             cfg.id = Roo.id();
22262             
22263             if (this.cls) {
22264                 cfg.cls += ' ' + this.cls;
22265             }
22266             if (this.style) {
22267                 cfg.style = this.style;
22268             }
22269             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22270             
22271             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22272         }
22273         
22274         this.el.select('>button.close').on('click', this.hide, this);
22275         
22276     },
22277     
22278     show : function()
22279     {
22280         if (!this.rendered) {
22281             this.render();
22282         }
22283         
22284         this.el.show();
22285         
22286         this.fireEvent('show', this);
22287         
22288     },
22289     
22290     hide : function()
22291     {
22292         if (!this.rendered) {
22293             this.render();
22294         }
22295         
22296         this.el.hide();
22297         
22298         this.fireEvent('hide', this);
22299     },
22300     
22301     update : function()
22302     {
22303 //        var e = this.el.dom.firstChild;
22304 //        
22305 //        if(this.closable){
22306 //            e = e.nextSibling;
22307 //        }
22308 //        
22309 //        e.data = this.html || '';
22310
22311         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22312     }
22313    
22314 });
22315
22316  
22317
22318      /*
22319  * - LGPL
22320  *
22321  * Graph
22322  * 
22323  */
22324
22325
22326 /**
22327  * @class Roo.bootstrap.Graph
22328  * @extends Roo.bootstrap.Component
22329  * Bootstrap Graph class
22330 > Prameters
22331  -sm {number} sm 4
22332  -md {number} md 5
22333  @cfg {String} graphtype  bar | vbar | pie
22334  @cfg {number} g_x coodinator | centre x (pie)
22335  @cfg {number} g_y coodinator | centre y (pie)
22336  @cfg {number} g_r radius (pie)
22337  @cfg {number} g_height height of the chart (respected by all elements in the set)
22338  @cfg {number} g_width width of the chart (respected by all elements in the set)
22339  @cfg {Object} title The title of the chart
22340     
22341  -{Array}  values
22342  -opts (object) options for the chart 
22343      o {
22344      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22345      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22346      o vgutter (number)
22347      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.
22348      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22349      o to
22350      o stretch (boolean)
22351      o }
22352  -opts (object) options for the pie
22353      o{
22354      o cut
22355      o startAngle (number)
22356      o endAngle (number)
22357      } 
22358  *
22359  * @constructor
22360  * Create a new Input
22361  * @param {Object} config The config object
22362  */
22363
22364 Roo.bootstrap.Graph = function(config){
22365     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22366     
22367     this.addEvents({
22368         // img events
22369         /**
22370          * @event click
22371          * The img click event for the img.
22372          * @param {Roo.EventObject} e
22373          */
22374         "click" : true
22375     });
22376 };
22377
22378 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22379     
22380     sm: 4,
22381     md: 5,
22382     graphtype: 'bar',
22383     g_height: 250,
22384     g_width: 400,
22385     g_x: 50,
22386     g_y: 50,
22387     g_r: 30,
22388     opts:{
22389         //g_colors: this.colors,
22390         g_type: 'soft',
22391         g_gutter: '20%'
22392
22393     },
22394     title : false,
22395
22396     getAutoCreate : function(){
22397         
22398         var cfg = {
22399             tag: 'div',
22400             html : null
22401         }
22402         
22403         
22404         return  cfg;
22405     },
22406
22407     onRender : function(ct,position){
22408         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22409         this.raphael = Raphael(this.el.dom);
22410         
22411                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22412                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22413                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22414                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22415                 /*
22416                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22417                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22418                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22419                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22420                 
22421                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22422                 r.barchart(330, 10, 300, 220, data1);
22423                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22424                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22425                 */
22426                 
22427                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22428                 // r.barchart(30, 30, 560, 250,  xdata, {
22429                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22430                 //     axis : "0 0 1 1",
22431                 //     axisxlabels :  xdata
22432                 //     //yvalues : cols,
22433                    
22434                 // });
22435 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22436 //        
22437 //        this.load(null,xdata,{
22438 //                axis : "0 0 1 1",
22439 //                axisxlabels :  xdata
22440 //                });
22441
22442     },
22443
22444     load : function(graphtype,xdata,opts){
22445         this.raphael.clear();
22446         if(!graphtype) {
22447             graphtype = this.graphtype;
22448         }
22449         if(!opts){
22450             opts = this.opts;
22451         }
22452         var r = this.raphael,
22453             fin = function () {
22454                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22455             },
22456             fout = function () {
22457                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22458             },
22459             pfin = function() {
22460                 this.sector.stop();
22461                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22462
22463                 if (this.label) {
22464                     this.label[0].stop();
22465                     this.label[0].attr({ r: 7.5 });
22466                     this.label[1].attr({ "font-weight": 800 });
22467                 }
22468             },
22469             pfout = function() {
22470                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22471
22472                 if (this.label) {
22473                     this.label[0].animate({ r: 5 }, 500, "bounce");
22474                     this.label[1].attr({ "font-weight": 400 });
22475                 }
22476             };
22477
22478         switch(graphtype){
22479             case 'bar':
22480                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22481                 break;
22482             case 'hbar':
22483                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22484                 break;
22485             case 'pie':
22486 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22487 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22488 //            
22489                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22490                 
22491                 break;
22492
22493         }
22494         
22495         if(this.title){
22496             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22497         }
22498         
22499     },
22500     
22501     setTitle: function(o)
22502     {
22503         this.title = o;
22504     },
22505     
22506     initEvents: function() {
22507         
22508         if(!this.href){
22509             this.el.on('click', this.onClick, this);
22510         }
22511     },
22512     
22513     onClick : function(e)
22514     {
22515         Roo.log('img onclick');
22516         this.fireEvent('click', this, e);
22517     }
22518    
22519 });
22520
22521  
22522 /*
22523  * - LGPL
22524  *
22525  * numberBox
22526  * 
22527  */
22528 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22529
22530 /**
22531  * @class Roo.bootstrap.dash.NumberBox
22532  * @extends Roo.bootstrap.Component
22533  * Bootstrap NumberBox class
22534  * @cfg {String} headline Box headline
22535  * @cfg {String} content Box content
22536  * @cfg {String} icon Box icon
22537  * @cfg {String} footer Footer text
22538  * @cfg {String} fhref Footer href
22539  * 
22540  * @constructor
22541  * Create a new NumberBox
22542  * @param {Object} config The config object
22543  */
22544
22545
22546 Roo.bootstrap.dash.NumberBox = function(config){
22547     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22548     
22549 };
22550
22551 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22552     
22553     headline : '',
22554     content : '',
22555     icon : '',
22556     footer : '',
22557     fhref : '',
22558     ficon : '',
22559     
22560     getAutoCreate : function(){
22561         
22562         var cfg = {
22563             tag : 'div',
22564             cls : 'small-box ',
22565             cn : [
22566                 {
22567                     tag : 'div',
22568                     cls : 'inner',
22569                     cn :[
22570                         {
22571                             tag : 'h3',
22572                             cls : 'roo-headline',
22573                             html : this.headline
22574                         },
22575                         {
22576                             tag : 'p',
22577                             cls : 'roo-content',
22578                             html : this.content
22579                         }
22580                     ]
22581                 }
22582             ]
22583         }
22584         
22585         if(this.icon){
22586             cfg.cn.push({
22587                 tag : 'div',
22588                 cls : 'icon',
22589                 cn :[
22590                     {
22591                         tag : 'i',
22592                         cls : 'ion ' + this.icon
22593                     }
22594                 ]
22595             });
22596         }
22597         
22598         if(this.footer){
22599             var footer = {
22600                 tag : 'a',
22601                 cls : 'small-box-footer',
22602                 href : this.fhref || '#',
22603                 html : this.footer
22604             };
22605             
22606             cfg.cn.push(footer);
22607             
22608         }
22609         
22610         return  cfg;
22611     },
22612
22613     onRender : function(ct,position){
22614         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22615
22616
22617        
22618                 
22619     },
22620
22621     setHeadline: function (value)
22622     {
22623         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22624     },
22625     
22626     setFooter: function (value, href)
22627     {
22628         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22629         
22630         if(href){
22631             this.el.select('a.small-box-footer',true).first().attr('href', href);
22632         }
22633         
22634     },
22635
22636     setContent: function (value)
22637     {
22638         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22639     },
22640
22641     initEvents: function() 
22642     {   
22643         
22644     }
22645     
22646 });
22647
22648  
22649 /*
22650  * - LGPL
22651  *
22652  * TabBox
22653  * 
22654  */
22655 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22656
22657 /**
22658  * @class Roo.bootstrap.dash.TabBox
22659  * @extends Roo.bootstrap.Component
22660  * Bootstrap TabBox class
22661  * @cfg {String} title Title of the TabBox
22662  * @cfg {String} icon Icon of the TabBox
22663  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22664  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22665  * 
22666  * @constructor
22667  * Create a new TabBox
22668  * @param {Object} config The config object
22669  */
22670
22671
22672 Roo.bootstrap.dash.TabBox = function(config){
22673     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22674     this.addEvents({
22675         // raw events
22676         /**
22677          * @event addpane
22678          * When a pane is added
22679          * @param {Roo.bootstrap.dash.TabPane} pane
22680          */
22681         "addpane" : true,
22682         /**
22683          * @event activatepane
22684          * When a pane is activated
22685          * @param {Roo.bootstrap.dash.TabPane} pane
22686          */
22687         "activatepane" : true
22688         
22689          
22690     });
22691     
22692     this.panes = [];
22693 };
22694
22695 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22696
22697     title : '',
22698     icon : false,
22699     showtabs : true,
22700     tabScrollable : false,
22701     
22702     getChildContainer : function()
22703     {
22704         return this.el.select('.tab-content', true).first();
22705     },
22706     
22707     getAutoCreate : function(){
22708         
22709         var header = {
22710             tag: 'li',
22711             cls: 'pull-left header',
22712             html: this.title,
22713             cn : []
22714         };
22715         
22716         if(this.icon){
22717             header.cn.push({
22718                 tag: 'i',
22719                 cls: 'fa ' + this.icon
22720             });
22721         }
22722         
22723         var h = {
22724             tag: 'ul',
22725             cls: 'nav nav-tabs pull-right',
22726             cn: [
22727                 header
22728             ]
22729         };
22730         
22731         if(this.tabScrollable){
22732             h = {
22733                 tag: 'div',
22734                 cls: 'tab-header',
22735                 cn: [
22736                     {
22737                         tag: 'ul',
22738                         cls: 'nav nav-tabs pull-right',
22739                         cn: [
22740                             header
22741                         ]
22742                     }
22743                 ]
22744             }
22745         }
22746         
22747         var cfg = {
22748             tag: 'div',
22749             cls: 'nav-tabs-custom',
22750             cn: [
22751                 h,
22752                 {
22753                     tag: 'div',
22754                     cls: 'tab-content no-padding',
22755                     cn: []
22756                 }
22757             ]
22758         }
22759
22760         return  cfg;
22761     },
22762     initEvents : function()
22763     {
22764         //Roo.log('add add pane handler');
22765         this.on('addpane', this.onAddPane, this);
22766     },
22767      /**
22768      * Updates the box title
22769      * @param {String} html to set the title to.
22770      */
22771     setTitle : function(value)
22772     {
22773         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22774     },
22775     onAddPane : function(pane)
22776     {
22777         this.panes.push(pane);
22778         //Roo.log('addpane');
22779         //Roo.log(pane);
22780         // tabs are rendere left to right..
22781         if(!this.showtabs){
22782             return;
22783         }
22784         
22785         var ctr = this.el.select('.nav-tabs', true).first();
22786          
22787          
22788         var existing = ctr.select('.nav-tab',true);
22789         var qty = existing.getCount();;
22790         
22791         
22792         var tab = ctr.createChild({
22793             tag : 'li',
22794             cls : 'nav-tab' + (qty ? '' : ' active'),
22795             cn : [
22796                 {
22797                     tag : 'a',
22798                     href:'#',
22799                     html : pane.title
22800                 }
22801             ]
22802         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22803         pane.tab = tab;
22804         
22805         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22806         if (!qty) {
22807             pane.el.addClass('active');
22808         }
22809         
22810                 
22811     },
22812     onTabClick : function(ev,un,ob,pane)
22813     {
22814         //Roo.log('tab - prev default');
22815         ev.preventDefault();
22816         
22817         
22818         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22819         pane.tab.addClass('active');
22820         //Roo.log(pane.title);
22821         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22822         // technically we should have a deactivate event.. but maybe add later.
22823         // and it should not de-activate the selected tab...
22824         this.fireEvent('activatepane', pane);
22825         pane.el.addClass('active');
22826         pane.fireEvent('activate');
22827         
22828         
22829     },
22830     
22831     getActivePane : function()
22832     {
22833         var r = false;
22834         Roo.each(this.panes, function(p) {
22835             if(p.el.hasClass('active')){
22836                 r = p;
22837                 return false;
22838             }
22839             
22840             return;
22841         });
22842         
22843         return r;
22844     }
22845     
22846     
22847 });
22848
22849  
22850 /*
22851  * - LGPL
22852  *
22853  * Tab pane
22854  * 
22855  */
22856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22857 /**
22858  * @class Roo.bootstrap.TabPane
22859  * @extends Roo.bootstrap.Component
22860  * Bootstrap TabPane class
22861  * @cfg {Boolean} active (false | true) Default false
22862  * @cfg {String} title title of panel
22863
22864  * 
22865  * @constructor
22866  * Create a new TabPane
22867  * @param {Object} config The config object
22868  */
22869
22870 Roo.bootstrap.dash.TabPane = function(config){
22871     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22872     
22873     this.addEvents({
22874         // raw events
22875         /**
22876          * @event activate
22877          * When a pane is activated
22878          * @param {Roo.bootstrap.dash.TabPane} pane
22879          */
22880         "activate" : true
22881          
22882     });
22883 };
22884
22885 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22886     
22887     active : false,
22888     title : '',
22889     
22890     // the tabBox that this is attached to.
22891     tab : false,
22892      
22893     getAutoCreate : function() 
22894     {
22895         var cfg = {
22896             tag: 'div',
22897             cls: 'tab-pane'
22898         }
22899         
22900         if(this.active){
22901             cfg.cls += ' active';
22902         }
22903         
22904         return cfg;
22905     },
22906     initEvents  : function()
22907     {
22908         //Roo.log('trigger add pane handler');
22909         this.parent().fireEvent('addpane', this)
22910     },
22911     
22912      /**
22913      * Updates the tab title 
22914      * @param {String} html to set the title to.
22915      */
22916     setTitle: function(str)
22917     {
22918         if (!this.tab) {
22919             return;
22920         }
22921         this.title = str;
22922         this.tab.select('a', true).first().dom.innerHTML = str;
22923         
22924     }
22925     
22926     
22927     
22928 });
22929
22930  
22931
22932
22933  /*
22934  * - LGPL
22935  *
22936  * menu
22937  * 
22938  */
22939 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22940
22941 /**
22942  * @class Roo.bootstrap.menu.Menu
22943  * @extends Roo.bootstrap.Component
22944  * Bootstrap Menu class - container for Menu
22945  * @cfg {String} html Text of the menu
22946  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22947  * @cfg {String} icon Font awesome icon
22948  * @cfg {String} pos Menu align to (top | bottom) default bottom
22949  * 
22950  * 
22951  * @constructor
22952  * Create a new Menu
22953  * @param {Object} config The config object
22954  */
22955
22956
22957 Roo.bootstrap.menu.Menu = function(config){
22958     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22959     
22960     this.addEvents({
22961         /**
22962          * @event beforeshow
22963          * Fires before this menu is displayed
22964          * @param {Roo.bootstrap.menu.Menu} this
22965          */
22966         beforeshow : true,
22967         /**
22968          * @event beforehide
22969          * Fires before this menu is hidden
22970          * @param {Roo.bootstrap.menu.Menu} this
22971          */
22972         beforehide : true,
22973         /**
22974          * @event show
22975          * Fires after this menu is displayed
22976          * @param {Roo.bootstrap.menu.Menu} this
22977          */
22978         show : true,
22979         /**
22980          * @event hide
22981          * Fires after this menu is hidden
22982          * @param {Roo.bootstrap.menu.Menu} this
22983          */
22984         hide : true,
22985         /**
22986          * @event click
22987          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22988          * @param {Roo.bootstrap.menu.Menu} this
22989          * @param {Roo.EventObject} e
22990          */
22991         click : true
22992     });
22993     
22994 };
22995
22996 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22997     
22998     submenu : false,
22999     html : '',
23000     weight : 'default',
23001     icon : false,
23002     pos : 'bottom',
23003     
23004     
23005     getChildContainer : function() {
23006         if(this.isSubMenu){
23007             return this.el;
23008         }
23009         
23010         return this.el.select('ul.dropdown-menu', true).first();  
23011     },
23012     
23013     getAutoCreate : function()
23014     {
23015         var text = [
23016             {
23017                 tag : 'span',
23018                 cls : 'roo-menu-text',
23019                 html : this.html
23020             }
23021         ];
23022         
23023         if(this.icon){
23024             text.unshift({
23025                 tag : 'i',
23026                 cls : 'fa ' + this.icon
23027             })
23028         }
23029         
23030         
23031         var cfg = {
23032             tag : 'div',
23033             cls : 'btn-group',
23034             cn : [
23035                 {
23036                     tag : 'button',
23037                     cls : 'dropdown-button btn btn-' + this.weight,
23038                     cn : text
23039                 },
23040                 {
23041                     tag : 'button',
23042                     cls : 'dropdown-toggle btn btn-' + this.weight,
23043                     cn : [
23044                         {
23045                             tag : 'span',
23046                             cls : 'caret'
23047                         }
23048                     ]
23049                 },
23050                 {
23051                     tag : 'ul',
23052                     cls : 'dropdown-menu'
23053                 }
23054             ]
23055             
23056         };
23057         
23058         if(this.pos == 'top'){
23059             cfg.cls += ' dropup';
23060         }
23061         
23062         if(this.isSubMenu){
23063             cfg = {
23064                 tag : 'ul',
23065                 cls : 'dropdown-menu'
23066             }
23067         }
23068         
23069         return cfg;
23070     },
23071     
23072     onRender : function(ct, position)
23073     {
23074         this.isSubMenu = ct.hasClass('dropdown-submenu');
23075         
23076         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23077     },
23078     
23079     initEvents : function() 
23080     {
23081         if(this.isSubMenu){
23082             return;
23083         }
23084         
23085         this.hidden = true;
23086         
23087         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23088         this.triggerEl.on('click', this.onTriggerPress, this);
23089         
23090         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23091         this.buttonEl.on('click', this.onClick, this);
23092         
23093     },
23094     
23095     list : function()
23096     {
23097         if(this.isSubMenu){
23098             return this.el;
23099         }
23100         
23101         return this.el.select('ul.dropdown-menu', true).first();
23102     },
23103     
23104     onClick : function(e)
23105     {
23106         this.fireEvent("click", this, e);
23107     },
23108     
23109     onTriggerPress  : function(e)
23110     {   
23111         if (this.isVisible()) {
23112             this.hide();
23113         } else {
23114             this.show();
23115         }
23116     },
23117     
23118     isVisible : function(){
23119         return !this.hidden;
23120     },
23121     
23122     show : function()
23123     {
23124         this.fireEvent("beforeshow", this);
23125         
23126         this.hidden = false;
23127         this.el.addClass('open');
23128         
23129         Roo.get(document).on("mouseup", this.onMouseUp, this);
23130         
23131         this.fireEvent("show", this);
23132         
23133         
23134     },
23135     
23136     hide : function()
23137     {
23138         this.fireEvent("beforehide", this);
23139         
23140         this.hidden = true;
23141         this.el.removeClass('open');
23142         
23143         Roo.get(document).un("mouseup", this.onMouseUp);
23144         
23145         this.fireEvent("hide", this);
23146     },
23147     
23148     onMouseUp : function()
23149     {
23150         this.hide();
23151     }
23152     
23153 });
23154
23155  
23156  /*
23157  * - LGPL
23158  *
23159  * menu item
23160  * 
23161  */
23162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23163
23164 /**
23165  * @class Roo.bootstrap.menu.Item
23166  * @extends Roo.bootstrap.Component
23167  * Bootstrap MenuItem class
23168  * @cfg {Boolean} submenu (true | false) default false
23169  * @cfg {String} html text of the item
23170  * @cfg {String} href the link
23171  * @cfg {Boolean} disable (true | false) default false
23172  * @cfg {Boolean} preventDefault (true | false) default true
23173  * @cfg {String} icon Font awesome icon
23174  * @cfg {String} pos Submenu align to (left | right) default right 
23175  * 
23176  * 
23177  * @constructor
23178  * Create a new Item
23179  * @param {Object} config The config object
23180  */
23181
23182
23183 Roo.bootstrap.menu.Item = function(config){
23184     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23185     this.addEvents({
23186         /**
23187          * @event mouseover
23188          * Fires when the mouse is hovering over this menu
23189          * @param {Roo.bootstrap.menu.Item} this
23190          * @param {Roo.EventObject} e
23191          */
23192         mouseover : true,
23193         /**
23194          * @event mouseout
23195          * Fires when the mouse exits this menu
23196          * @param {Roo.bootstrap.menu.Item} this
23197          * @param {Roo.EventObject} e
23198          */
23199         mouseout : true,
23200         // raw events
23201         /**
23202          * @event click
23203          * The raw click event for the entire grid.
23204          * @param {Roo.EventObject} e
23205          */
23206         click : true
23207     });
23208 };
23209
23210 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23211     
23212     submenu : false,
23213     href : '',
23214     html : '',
23215     preventDefault: true,
23216     disable : false,
23217     icon : false,
23218     pos : 'right',
23219     
23220     getAutoCreate : function()
23221     {
23222         var text = [
23223             {
23224                 tag : 'span',
23225                 cls : 'roo-menu-item-text',
23226                 html : this.html
23227             }
23228         ];
23229         
23230         if(this.icon){
23231             text.unshift({
23232                 tag : 'i',
23233                 cls : 'fa ' + this.icon
23234             })
23235         }
23236         
23237         var cfg = {
23238             tag : 'li',
23239             cn : [
23240                 {
23241                     tag : 'a',
23242                     href : this.href || '#',
23243                     cn : text
23244                 }
23245             ]
23246         };
23247         
23248         if(this.disable){
23249             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23250         }
23251         
23252         if(this.submenu){
23253             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23254             
23255             if(this.pos == 'left'){
23256                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23257             }
23258         }
23259         
23260         return cfg;
23261     },
23262     
23263     initEvents : function() 
23264     {
23265         this.el.on('mouseover', this.onMouseOver, this);
23266         this.el.on('mouseout', this.onMouseOut, this);
23267         
23268         this.el.select('a', true).first().on('click', this.onClick, this);
23269         
23270     },
23271     
23272     onClick : function(e)
23273     {
23274         if(this.preventDefault){
23275             e.preventDefault();
23276         }
23277         
23278         this.fireEvent("click", this, e);
23279     },
23280     
23281     onMouseOver : function(e)
23282     {
23283         if(this.submenu && this.pos == 'left'){
23284             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23285         }
23286         
23287         this.fireEvent("mouseover", this, e);
23288     },
23289     
23290     onMouseOut : function(e)
23291     {
23292         this.fireEvent("mouseout", this, e);
23293     }
23294 });
23295
23296  
23297
23298  /*
23299  * - LGPL
23300  *
23301  * menu separator
23302  * 
23303  */
23304 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23305
23306 /**
23307  * @class Roo.bootstrap.menu.Separator
23308  * @extends Roo.bootstrap.Component
23309  * Bootstrap Separator class
23310  * 
23311  * @constructor
23312  * Create a new Separator
23313  * @param {Object} config The config object
23314  */
23315
23316
23317 Roo.bootstrap.menu.Separator = function(config){
23318     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23319 };
23320
23321 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23322     
23323     getAutoCreate : function(){
23324         var cfg = {
23325             tag : 'li',
23326             cls: 'divider'
23327         };
23328         
23329         return cfg;
23330     }
23331    
23332 });
23333
23334  
23335
23336  /*
23337  * - LGPL
23338  *
23339  * Tooltip
23340  * 
23341  */
23342
23343 /**
23344  * @class Roo.bootstrap.Tooltip
23345  * Bootstrap Tooltip class
23346  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23347  * to determine which dom element triggers the tooltip.
23348  * 
23349  * It needs to add support for additional attributes like tooltip-position
23350  * 
23351  * @constructor
23352  * Create a new Toolti
23353  * @param {Object} config The config object
23354  */
23355
23356 Roo.bootstrap.Tooltip = function(config){
23357     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23358 };
23359
23360 Roo.apply(Roo.bootstrap.Tooltip, {
23361     /**
23362      * @function init initialize tooltip monitoring.
23363      * @static
23364      */
23365     currentEl : false,
23366     currentTip : false,
23367     currentRegion : false,
23368     
23369     //  init : delay?
23370     
23371     init : function()
23372     {
23373         Roo.get(document).on('mouseover', this.enter ,this);
23374         Roo.get(document).on('mouseout', this.leave, this);
23375          
23376         
23377         this.currentTip = new Roo.bootstrap.Tooltip();
23378     },
23379     
23380     enter : function(ev)
23381     {
23382         var dom = ev.getTarget();
23383         
23384         //Roo.log(['enter',dom]);
23385         var el = Roo.fly(dom);
23386         if (this.currentEl) {
23387             //Roo.log(dom);
23388             //Roo.log(this.currentEl);
23389             //Roo.log(this.currentEl.contains(dom));
23390             if (this.currentEl == el) {
23391                 return;
23392             }
23393             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23394                 return;
23395             }
23396
23397         }
23398         
23399         if (this.currentTip.el) {
23400             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23401         }    
23402         //Roo.log(ev);
23403         var bindEl = el;
23404         
23405         // you can not look for children, as if el is the body.. then everythign is the child..
23406         if (!el.attr('tooltip')) { //
23407             if (!el.select("[tooltip]").elements.length) {
23408                 return;
23409             }
23410             // is the mouse over this child...?
23411             bindEl = el.select("[tooltip]").first();
23412             var xy = ev.getXY();
23413             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23414                 //Roo.log("not in region.");
23415                 return;
23416             }
23417             //Roo.log("child element over..");
23418             
23419         }
23420         this.currentEl = bindEl;
23421         this.currentTip.bind(bindEl);
23422         this.currentRegion = Roo.lib.Region.getRegion(dom);
23423         this.currentTip.enter();
23424         
23425     },
23426     leave : function(ev)
23427     {
23428         var dom = ev.getTarget();
23429         //Roo.log(['leave',dom]);
23430         if (!this.currentEl) {
23431             return;
23432         }
23433         
23434         
23435         if (dom != this.currentEl.dom) {
23436             return;
23437         }
23438         var xy = ev.getXY();
23439         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23440             return;
23441         }
23442         // only activate leave if mouse cursor is outside... bounding box..
23443         
23444         
23445         
23446         
23447         if (this.currentTip) {
23448             this.currentTip.leave();
23449         }
23450         //Roo.log('clear currentEl');
23451         this.currentEl = false;
23452         
23453         
23454     },
23455     alignment : {
23456         'left' : ['r-l', [-2,0], 'right'],
23457         'right' : ['l-r', [2,0], 'left'],
23458         'bottom' : ['t-b', [0,2], 'top'],
23459         'top' : [ 'b-t', [0,-2], 'bottom']
23460     }
23461     
23462 });
23463
23464
23465 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23466     
23467     
23468     bindEl : false,
23469     
23470     delay : null, // can be { show : 300 , hide: 500}
23471     
23472     timeout : null,
23473     
23474     hoverState : null, //???
23475     
23476     placement : 'bottom', 
23477     
23478     getAutoCreate : function(){
23479     
23480         var cfg = {
23481            cls : 'tooltip',
23482            role : 'tooltip',
23483            cn : [
23484                 {
23485                     cls : 'tooltip-arrow'
23486                 },
23487                 {
23488                     cls : 'tooltip-inner'
23489                 }
23490            ]
23491         };
23492         
23493         return cfg;
23494     },
23495     bind : function(el)
23496     {
23497         this.bindEl = el;
23498     },
23499       
23500     
23501     enter : function () {
23502        
23503         if (this.timeout != null) {
23504             clearTimeout(this.timeout);
23505         }
23506         
23507         this.hoverState = 'in';
23508          //Roo.log("enter - show");
23509         if (!this.delay || !this.delay.show) {
23510             this.show();
23511             return;
23512         }
23513         var _t = this;
23514         this.timeout = setTimeout(function () {
23515             if (_t.hoverState == 'in') {
23516                 _t.show();
23517             }
23518         }, this.delay.show);
23519     },
23520     leave : function()
23521     {
23522         clearTimeout(this.timeout);
23523     
23524         this.hoverState = 'out';
23525          if (!this.delay || !this.delay.hide) {
23526             this.hide();
23527             return;
23528         }
23529        
23530         var _t = this;
23531         this.timeout = setTimeout(function () {
23532             //Roo.log("leave - timeout");
23533             
23534             if (_t.hoverState == 'out') {
23535                 _t.hide();
23536                 Roo.bootstrap.Tooltip.currentEl = false;
23537             }
23538         }, delay);
23539     },
23540     
23541     show : function ()
23542     {
23543         if (!this.el) {
23544             this.render(document.body);
23545         }
23546         // set content.
23547         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23548         
23549         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23550         
23551         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23552         
23553         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23554         
23555         var placement = typeof this.placement == 'function' ?
23556             this.placement.call(this, this.el, on_el) :
23557             this.placement;
23558             
23559         var autoToken = /\s?auto?\s?/i;
23560         var autoPlace = autoToken.test(placement);
23561         if (autoPlace) {
23562             placement = placement.replace(autoToken, '') || 'top';
23563         }
23564         
23565         //this.el.detach()
23566         //this.el.setXY([0,0]);
23567         this.el.show();
23568         //this.el.dom.style.display='block';
23569         
23570         //this.el.appendTo(on_el);
23571         
23572         var p = this.getPosition();
23573         var box = this.el.getBox();
23574         
23575         if (autoPlace) {
23576             // fixme..
23577         }
23578         
23579         var align = Roo.bootstrap.Tooltip.alignment[placement];
23580         
23581         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23582         
23583         if(placement == 'top' || placement == 'bottom'){
23584             if(xy[0] < 0){
23585                 placement = 'right';
23586             }
23587             
23588             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23589                 placement = 'left';
23590             }
23591         }
23592         
23593         align = Roo.bootstrap.Tooltip.alignment[placement];
23594         
23595         this.el.alignTo(this.bindEl, align[0],align[1]);
23596         //var arrow = this.el.select('.arrow',true).first();
23597         //arrow.set(align[2], 
23598         
23599         this.el.addClass(placement);
23600         
23601         this.el.addClass('in fade');
23602         
23603         this.hoverState = null;
23604         
23605         if (this.el.hasClass('fade')) {
23606             // fade it?
23607         }
23608         
23609     },
23610     hide : function()
23611     {
23612          
23613         if (!this.el) {
23614             return;
23615         }
23616         //this.el.setXY([0,0]);
23617         this.el.removeClass('in');
23618         //this.el.hide();
23619         
23620     }
23621     
23622 });
23623  
23624
23625  /*
23626  * - LGPL
23627  *
23628  * Location Picker
23629  * 
23630  */
23631
23632 /**
23633  * @class Roo.bootstrap.LocationPicker
23634  * @extends Roo.bootstrap.Component
23635  * Bootstrap LocationPicker class
23636  * @cfg {Number} latitude Position when init default 0
23637  * @cfg {Number} longitude Position when init default 0
23638  * @cfg {Number} zoom default 15
23639  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23640  * @cfg {Boolean} mapTypeControl default false
23641  * @cfg {Boolean} disableDoubleClickZoom default false
23642  * @cfg {Boolean} scrollwheel default true
23643  * @cfg {Boolean} streetViewControl default false
23644  * @cfg {Number} radius default 0
23645  * @cfg {String} locationName
23646  * @cfg {Boolean} draggable default true
23647  * @cfg {Boolean} enableAutocomplete default false
23648  * @cfg {Boolean} enableReverseGeocode default true
23649  * @cfg {String} markerTitle
23650  * 
23651  * @constructor
23652  * Create a new LocationPicker
23653  * @param {Object} config The config object
23654  */
23655
23656
23657 Roo.bootstrap.LocationPicker = function(config){
23658     
23659     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23660     
23661     this.addEvents({
23662         /**
23663          * @event initial
23664          * Fires when the picker initialized.
23665          * @param {Roo.bootstrap.LocationPicker} this
23666          * @param {Google Location} location
23667          */
23668         initial : true,
23669         /**
23670          * @event positionchanged
23671          * Fires when the picker position changed.
23672          * @param {Roo.bootstrap.LocationPicker} this
23673          * @param {Google Location} location
23674          */
23675         positionchanged : true,
23676         /**
23677          * @event resize
23678          * Fires when the map resize.
23679          * @param {Roo.bootstrap.LocationPicker} this
23680          */
23681         resize : true,
23682         /**
23683          * @event show
23684          * Fires when the map show.
23685          * @param {Roo.bootstrap.LocationPicker} this
23686          */
23687         show : true,
23688         /**
23689          * @event hide
23690          * Fires when the map hide.
23691          * @param {Roo.bootstrap.LocationPicker} this
23692          */
23693         hide : true,
23694         /**
23695          * @event mapClick
23696          * Fires when click the map.
23697          * @param {Roo.bootstrap.LocationPicker} this
23698          * @param {Map event} e
23699          */
23700         mapClick : true,
23701         /**
23702          * @event mapRightClick
23703          * Fires when right click the map.
23704          * @param {Roo.bootstrap.LocationPicker} this
23705          * @param {Map event} e
23706          */
23707         mapRightClick : true,
23708         /**
23709          * @event markerClick
23710          * Fires when click the marker.
23711          * @param {Roo.bootstrap.LocationPicker} this
23712          * @param {Map event} e
23713          */
23714         markerClick : true,
23715         /**
23716          * @event markerRightClick
23717          * Fires when right click the marker.
23718          * @param {Roo.bootstrap.LocationPicker} this
23719          * @param {Map event} e
23720          */
23721         markerRightClick : true,
23722         /**
23723          * @event OverlayViewDraw
23724          * Fires when OverlayView Draw
23725          * @param {Roo.bootstrap.LocationPicker} this
23726          */
23727         OverlayViewDraw : true,
23728         /**
23729          * @event OverlayViewOnAdd
23730          * Fires when OverlayView Draw
23731          * @param {Roo.bootstrap.LocationPicker} this
23732          */
23733         OverlayViewOnAdd : true,
23734         /**
23735          * @event OverlayViewOnRemove
23736          * Fires when OverlayView Draw
23737          * @param {Roo.bootstrap.LocationPicker} this
23738          */
23739         OverlayViewOnRemove : true,
23740         /**
23741          * @event OverlayViewShow
23742          * Fires when OverlayView Draw
23743          * @param {Roo.bootstrap.LocationPicker} this
23744          * @param {Pixel} cpx
23745          */
23746         OverlayViewShow : true,
23747         /**
23748          * @event OverlayViewHide
23749          * Fires when OverlayView Draw
23750          * @param {Roo.bootstrap.LocationPicker} this
23751          */
23752         OverlayViewHide : true,
23753         /**
23754          * @event loadexception
23755          * Fires when load google lib failed.
23756          * @param {Roo.bootstrap.LocationPicker} this
23757          */
23758         loadexception : true
23759     });
23760         
23761 };
23762
23763 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23764     
23765     gMapContext: false,
23766     
23767     latitude: 0,
23768     longitude: 0,
23769     zoom: 15,
23770     mapTypeId: false,
23771     mapTypeControl: false,
23772     disableDoubleClickZoom: false,
23773     scrollwheel: true,
23774     streetViewControl: false,
23775     radius: 0,
23776     locationName: '',
23777     draggable: true,
23778     enableAutocomplete: false,
23779     enableReverseGeocode: true,
23780     markerTitle: '',
23781     
23782     getAutoCreate: function()
23783     {
23784
23785         var cfg = {
23786             tag: 'div',
23787             cls: 'roo-location-picker'
23788         };
23789         
23790         return cfg
23791     },
23792     
23793     initEvents: function(ct, position)
23794     {       
23795         if(!this.el.getWidth() || this.isApplied()){
23796             return;
23797         }
23798         
23799         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23800         
23801         this.initial();
23802     },
23803     
23804     initial: function()
23805     {
23806         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23807             this.fireEvent('loadexception', this);
23808             return;
23809         }
23810         
23811         if(!this.mapTypeId){
23812             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23813         }
23814         
23815         this.gMapContext = this.GMapContext();
23816         
23817         this.initOverlayView();
23818         
23819         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23820         
23821         var _this = this;
23822                 
23823         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23824             _this.setPosition(_this.gMapContext.marker.position);
23825         });
23826         
23827         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23828             _this.fireEvent('mapClick', this, event);
23829             
23830         });
23831
23832         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23833             _this.fireEvent('mapRightClick', this, event);
23834             
23835         });
23836         
23837         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23838             _this.fireEvent('markerClick', this, event);
23839             
23840         });
23841
23842         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23843             _this.fireEvent('markerRightClick', this, event);
23844             
23845         });
23846         
23847         this.setPosition(this.gMapContext.location);
23848         
23849         this.fireEvent('initial', this, this.gMapContext.location);
23850     },
23851     
23852     initOverlayView: function()
23853     {
23854         var _this = this;
23855         
23856         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23857             
23858             draw: function()
23859             {
23860                 _this.fireEvent('OverlayViewDraw', _this);
23861             },
23862             
23863             onAdd: function()
23864             {
23865                 _this.fireEvent('OverlayViewOnAdd', _this);
23866             },
23867             
23868             onRemove: function()
23869             {
23870                 _this.fireEvent('OverlayViewOnRemove', _this);
23871             },
23872             
23873             show: function(cpx)
23874             {
23875                 _this.fireEvent('OverlayViewShow', _this, cpx);
23876             },
23877             
23878             hide: function()
23879             {
23880                 _this.fireEvent('OverlayViewHide', _this);
23881             }
23882             
23883         });
23884     },
23885     
23886     fromLatLngToContainerPixel: function(event)
23887     {
23888         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23889     },
23890     
23891     isApplied: function() 
23892     {
23893         return this.getGmapContext() == false ? false : true;
23894     },
23895     
23896     getGmapContext: function() 
23897     {
23898         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23899     },
23900     
23901     GMapContext: function() 
23902     {
23903         var position = new google.maps.LatLng(this.latitude, this.longitude);
23904         
23905         var _map = new google.maps.Map(this.el.dom, {
23906             center: position,
23907             zoom: this.zoom,
23908             mapTypeId: this.mapTypeId,
23909             mapTypeControl: this.mapTypeControl,
23910             disableDoubleClickZoom: this.disableDoubleClickZoom,
23911             scrollwheel: this.scrollwheel,
23912             streetViewControl: this.streetViewControl,
23913             locationName: this.locationName,
23914             draggable: this.draggable,
23915             enableAutocomplete: this.enableAutocomplete,
23916             enableReverseGeocode: this.enableReverseGeocode
23917         });
23918         
23919         var _marker = new google.maps.Marker({
23920             position: position,
23921             map: _map,
23922             title: this.markerTitle,
23923             draggable: this.draggable
23924         });
23925         
23926         return {
23927             map: _map,
23928             marker: _marker,
23929             circle: null,
23930             location: position,
23931             radius: this.radius,
23932             locationName: this.locationName,
23933             addressComponents: {
23934                 formatted_address: null,
23935                 addressLine1: null,
23936                 addressLine2: null,
23937                 streetName: null,
23938                 streetNumber: null,
23939                 city: null,
23940                 district: null,
23941                 state: null,
23942                 stateOrProvince: null
23943             },
23944             settings: this,
23945             domContainer: this.el.dom,
23946             geodecoder: new google.maps.Geocoder()
23947         };
23948     },
23949     
23950     drawCircle: function(center, radius, options) 
23951     {
23952         if (this.gMapContext.circle != null) {
23953             this.gMapContext.circle.setMap(null);
23954         }
23955         if (radius > 0) {
23956             radius *= 1;
23957             options = Roo.apply({}, options, {
23958                 strokeColor: "#0000FF",
23959                 strokeOpacity: .35,
23960                 strokeWeight: 2,
23961                 fillColor: "#0000FF",
23962                 fillOpacity: .2
23963             });
23964             
23965             options.map = this.gMapContext.map;
23966             options.radius = radius;
23967             options.center = center;
23968             this.gMapContext.circle = new google.maps.Circle(options);
23969             return this.gMapContext.circle;
23970         }
23971         
23972         return null;
23973     },
23974     
23975     setPosition: function(location) 
23976     {
23977         this.gMapContext.location = location;
23978         this.gMapContext.marker.setPosition(location);
23979         this.gMapContext.map.panTo(location);
23980         this.drawCircle(location, this.gMapContext.radius, {});
23981         
23982         var _this = this;
23983         
23984         if (this.gMapContext.settings.enableReverseGeocode) {
23985             this.gMapContext.geodecoder.geocode({
23986                 latLng: this.gMapContext.location
23987             }, function(results, status) {
23988                 
23989                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23990                     _this.gMapContext.locationName = results[0].formatted_address;
23991                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23992                     
23993                     _this.fireEvent('positionchanged', this, location);
23994                 }
23995             });
23996             
23997             return;
23998         }
23999         
24000         this.fireEvent('positionchanged', this, location);
24001     },
24002     
24003     resize: function()
24004     {
24005         google.maps.event.trigger(this.gMapContext.map, "resize");
24006         
24007         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24008         
24009         this.fireEvent('resize', this);
24010     },
24011     
24012     setPositionByLatLng: function(latitude, longitude)
24013     {
24014         this.setPosition(new google.maps.LatLng(latitude, longitude));
24015     },
24016     
24017     getCurrentPosition: function() 
24018     {
24019         return {
24020             latitude: this.gMapContext.location.lat(),
24021             longitude: this.gMapContext.location.lng()
24022         };
24023     },
24024     
24025     getAddressName: function() 
24026     {
24027         return this.gMapContext.locationName;
24028     },
24029     
24030     getAddressComponents: function() 
24031     {
24032         return this.gMapContext.addressComponents;
24033     },
24034     
24035     address_component_from_google_geocode: function(address_components) 
24036     {
24037         var result = {};
24038         
24039         for (var i = 0; i < address_components.length; i++) {
24040             var component = address_components[i];
24041             if (component.types.indexOf("postal_code") >= 0) {
24042                 result.postalCode = component.short_name;
24043             } else if (component.types.indexOf("street_number") >= 0) {
24044                 result.streetNumber = component.short_name;
24045             } else if (component.types.indexOf("route") >= 0) {
24046                 result.streetName = component.short_name;
24047             } else if (component.types.indexOf("neighborhood") >= 0) {
24048                 result.city = component.short_name;
24049             } else if (component.types.indexOf("locality") >= 0) {
24050                 result.city = component.short_name;
24051             } else if (component.types.indexOf("sublocality") >= 0) {
24052                 result.district = component.short_name;
24053             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24054                 result.stateOrProvince = component.short_name;
24055             } else if (component.types.indexOf("country") >= 0) {
24056                 result.country = component.short_name;
24057             }
24058         }
24059         
24060         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24061         result.addressLine2 = "";
24062         return result;
24063     },
24064     
24065     setZoomLevel: function(zoom)
24066     {
24067         this.gMapContext.map.setZoom(zoom);
24068     },
24069     
24070     show: function()
24071     {
24072         if(!this.el){
24073             return;
24074         }
24075         
24076         this.el.show();
24077         
24078         this.resize();
24079         
24080         this.fireEvent('show', this);
24081     },
24082     
24083     hide: function()
24084     {
24085         if(!this.el){
24086             return;
24087         }
24088         
24089         this.el.hide();
24090         
24091         this.fireEvent('hide', this);
24092     }
24093     
24094 });
24095
24096 Roo.apply(Roo.bootstrap.LocationPicker, {
24097     
24098     OverlayView : function(map, options)
24099     {
24100         options = options || {};
24101         
24102         this.setMap(map);
24103     }
24104     
24105     
24106 });/*
24107  * - LGPL
24108  *
24109  * Alert
24110  * 
24111  */
24112
24113 /**
24114  * @class Roo.bootstrap.Alert
24115  * @extends Roo.bootstrap.Component
24116  * Bootstrap Alert class
24117  * @cfg {String} title The title of alert
24118  * @cfg {String} html The content of alert
24119  * @cfg {String} weight (  success | info | warning | danger )
24120  * @cfg {String} faicon font-awesomeicon
24121  * 
24122  * @constructor
24123  * Create a new alert
24124  * @param {Object} config The config object
24125  */
24126
24127
24128 Roo.bootstrap.Alert = function(config){
24129     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24130     
24131 };
24132
24133 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24134     
24135     title: '',
24136     html: '',
24137     weight: false,
24138     faicon: false,
24139     
24140     getAutoCreate : function()
24141     {
24142         
24143         var cfg = {
24144             tag : 'div',
24145             cls : 'alert',
24146             cn : [
24147                 {
24148                     tag : 'i',
24149                     cls : 'roo-alert-icon'
24150                     
24151                 },
24152                 {
24153                     tag : 'b',
24154                     cls : 'roo-alert-title',
24155                     html : this.title
24156                 },
24157                 {
24158                     tag : 'span',
24159                     cls : 'roo-alert-text',
24160                     html : this.html
24161                 }
24162             ]
24163         };
24164         
24165         if(this.faicon){
24166             cfg.cn[0].cls += ' fa ' + this.faicon;
24167         }
24168         
24169         if(this.weight){
24170             cfg.cls += ' alert-' + this.weight;
24171         }
24172         
24173         return cfg;
24174     },
24175     
24176     initEvents: function() 
24177     {
24178         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24179     },
24180     
24181     setTitle : function(str)
24182     {
24183         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24184     },
24185     
24186     setText : function(str)
24187     {
24188         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24189     },
24190     
24191     setWeight : function(weight)
24192     {
24193         if(this.weight){
24194             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24195         }
24196         
24197         this.weight = weight;
24198         
24199         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24200     },
24201     
24202     setIcon : function(icon)
24203     {
24204         if(this.faicon){
24205             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24206         }
24207         
24208         this.faicon = icon;
24209         
24210         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24211     },
24212     
24213     hide: function() 
24214     {
24215         this.el.hide();   
24216     },
24217     
24218     show: function() 
24219     {  
24220         this.el.show();   
24221     }
24222     
24223 });
24224
24225  
24226 /*
24227 * Licence: LGPL
24228 */
24229
24230 /**
24231  * @class Roo.bootstrap.UploadCropbox
24232  * @extends Roo.bootstrap.Component
24233  * Bootstrap UploadCropbox class
24234  * @cfg {String} emptyText show when image has been loaded
24235  * @cfg {String} rotateNotify show when image too small to rotate
24236  * @cfg {Number} errorTimeout default 3000
24237  * @cfg {Number} minWidth default 300
24238  * @cfg {Number} minHeight default 300
24239  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24240  * @cfg {Boolean} isDocument (true|false) default false
24241  * @cfg {String} url action url
24242  * @cfg {String} paramName default 'imageUpload'
24243  * @cfg {String} method default POST
24244  * @cfg {Boolean} loadMask (true|false) default true
24245  * @cfg {Boolean} loadingText default 'Loading...'
24246  * 
24247  * @constructor
24248  * Create a new UploadCropbox
24249  * @param {Object} config The config object
24250  */
24251
24252 Roo.bootstrap.UploadCropbox = function(config){
24253     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24254     
24255     this.addEvents({
24256         /**
24257          * @event beforeselectfile
24258          * Fire before select file
24259          * @param {Roo.bootstrap.UploadCropbox} this
24260          */
24261         "beforeselectfile" : true,
24262         /**
24263          * @event initial
24264          * Fire after initEvent
24265          * @param {Roo.bootstrap.UploadCropbox} this
24266          */
24267         "initial" : true,
24268         /**
24269          * @event crop
24270          * Fire after initEvent
24271          * @param {Roo.bootstrap.UploadCropbox} this
24272          * @param {String} data
24273          */
24274         "crop" : true,
24275         /**
24276          * @event prepare
24277          * Fire when preparing the file data
24278          * @param {Roo.bootstrap.UploadCropbox} this
24279          * @param {Object} file
24280          */
24281         "prepare" : true,
24282         /**
24283          * @event exception
24284          * Fire when get exception
24285          * @param {Roo.bootstrap.UploadCropbox} this
24286          * @param {XMLHttpRequest} xhr
24287          */
24288         "exception" : true,
24289         /**
24290          * @event beforeloadcanvas
24291          * Fire before load the canvas
24292          * @param {Roo.bootstrap.UploadCropbox} this
24293          * @param {String} src
24294          */
24295         "beforeloadcanvas" : true,
24296         /**
24297          * @event trash
24298          * Fire when trash image
24299          * @param {Roo.bootstrap.UploadCropbox} this
24300          */
24301         "trash" : true,
24302         /**
24303          * @event download
24304          * Fire when download the image
24305          * @param {Roo.bootstrap.UploadCropbox} this
24306          */
24307         "download" : true,
24308         /**
24309          * @event footerbuttonclick
24310          * Fire when footerbuttonclick
24311          * @param {Roo.bootstrap.UploadCropbox} this
24312          * @param {String} type
24313          */
24314         "footerbuttonclick" : true,
24315         /**
24316          * @event resize
24317          * Fire when resize
24318          * @param {Roo.bootstrap.UploadCropbox} this
24319          */
24320         "resize" : true,
24321         /**
24322          * @event rotate
24323          * Fire when rotate the image
24324          * @param {Roo.bootstrap.UploadCropbox} this
24325          * @param {String} pos
24326          */
24327         "rotate" : true,
24328         /**
24329          * @event inspect
24330          * Fire when inspect the file
24331          * @param {Roo.bootstrap.UploadCropbox} this
24332          * @param {Object} file
24333          */
24334         "inspect" : true,
24335         /**
24336          * @event upload
24337          * Fire when xhr upload the file
24338          * @param {Roo.bootstrap.UploadCropbox} this
24339          * @param {Object} data
24340          */
24341         "upload" : true,
24342         /**
24343          * @event arrange
24344          * Fire when arrange the file data
24345          * @param {Roo.bootstrap.UploadCropbox} this
24346          * @param {Object} formData
24347          */
24348         "arrange" : true
24349     });
24350     
24351     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24352 };
24353
24354 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24355     
24356     emptyText : 'Click to upload image',
24357     rotateNotify : 'Image is too small to rotate',
24358     errorTimeout : 3000,
24359     scale : 0,
24360     baseScale : 1,
24361     rotate : 0,
24362     dragable : false,
24363     pinching : false,
24364     mouseX : 0,
24365     mouseY : 0,
24366     cropData : false,
24367     minWidth : 300,
24368     minHeight : 300,
24369     file : false,
24370     exif : {},
24371     baseRotate : 1,
24372     cropType : 'image/jpeg',
24373     buttons : false,
24374     canvasLoaded : false,
24375     isDocument : false,
24376     method : 'POST',
24377     paramName : 'imageUpload',
24378     loadMask : true,
24379     loadingText : 'Loading...',
24380     maskEl : false,
24381     
24382     getAutoCreate : function()
24383     {
24384         var cfg = {
24385             tag : 'div',
24386             cls : 'roo-upload-cropbox',
24387             cn : [
24388                 {
24389                     tag : 'input',
24390                     cls : 'roo-upload-cropbox-selector',
24391                     type : 'file'
24392                 },
24393                 {
24394                     tag : 'div',
24395                     cls : 'roo-upload-cropbox-body',
24396                     style : 'cursor:pointer',
24397                     cn : [
24398                         {
24399                             tag : 'div',
24400                             cls : 'roo-upload-cropbox-preview'
24401                         },
24402                         {
24403                             tag : 'div',
24404                             cls : 'roo-upload-cropbox-thumb'
24405                         },
24406                         {
24407                             tag : 'div',
24408                             cls : 'roo-upload-cropbox-empty-notify',
24409                             html : this.emptyText
24410                         },
24411                         {
24412                             tag : 'div',
24413                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24414                             html : this.rotateNotify
24415                         }
24416                     ]
24417                 },
24418                 {
24419                     tag : 'div',
24420                     cls : 'roo-upload-cropbox-footer',
24421                     cn : {
24422                         tag : 'div',
24423                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24424                         cn : []
24425                     }
24426                 }
24427             ]
24428         };
24429         
24430         return cfg;
24431     },
24432     
24433     onRender : function(ct, position)
24434     {
24435         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24436         
24437         if (this.buttons.length) {
24438             
24439             Roo.each(this.buttons, function(bb) {
24440                 
24441                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24442                 
24443                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24444                 
24445             }, this);
24446         }
24447         
24448         if(this.loadMask){
24449             this.maskEl = this.el;
24450         }
24451     },
24452     
24453     initEvents : function()
24454     {
24455         this.urlAPI = (window.createObjectURL && window) || 
24456                                 (window.URL && URL.revokeObjectURL && URL) || 
24457                                 (window.webkitURL && webkitURL);
24458                         
24459         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24460         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24461         
24462         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24463         this.selectorEl.hide();
24464         
24465         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24466         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24467         
24468         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24469         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24470         this.thumbEl.hide();
24471         
24472         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24473         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24474         
24475         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24476         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24477         this.errorEl.hide();
24478         
24479         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24480         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24481         this.footerEl.hide();
24482         
24483         this.setThumbBoxSize();
24484         
24485         this.bind();
24486         
24487         this.resize();
24488         
24489         this.fireEvent('initial', this);
24490     },
24491
24492     bind : function()
24493     {
24494         var _this = this;
24495         
24496         window.addEventListener("resize", function() { _this.resize(); } );
24497         
24498         this.bodyEl.on('click', this.beforeSelectFile, this);
24499         
24500         if(Roo.isTouch){
24501             this.bodyEl.on('touchstart', this.onTouchStart, this);
24502             this.bodyEl.on('touchmove', this.onTouchMove, this);
24503             this.bodyEl.on('touchend', this.onTouchEnd, this);
24504         }
24505         
24506         if(!Roo.isTouch){
24507             this.bodyEl.on('mousedown', this.onMouseDown, this);
24508             this.bodyEl.on('mousemove', this.onMouseMove, this);
24509             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24510             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24511             Roo.get(document).on('mouseup', this.onMouseUp, this);
24512         }
24513         
24514         this.selectorEl.on('change', this.onFileSelected, this);
24515     },
24516     
24517     reset : function()
24518     {    
24519         this.scale = 0;
24520         this.baseScale = 1;
24521         this.rotate = 0;
24522         this.baseRotate = 1;
24523         this.dragable = false;
24524         this.pinching = false;
24525         this.mouseX = 0;
24526         this.mouseY = 0;
24527         this.cropData = false;
24528         this.notifyEl.dom.innerHTML = this.emptyText;
24529         
24530         this.selectorEl.dom.value = '';
24531         
24532     },
24533     
24534     resize : function()
24535     {
24536         if(this.fireEvent('resize', this) != false){
24537             this.setThumbBoxPosition();
24538             this.setCanvasPosition();
24539         }
24540     },
24541     
24542     onFooterButtonClick : function(e, el, o, type)
24543     {
24544         switch (type) {
24545             case 'rotate-left' :
24546                 this.onRotateLeft(e);
24547                 break;
24548             case 'rotate-right' :
24549                 this.onRotateRight(e);
24550                 break;
24551             case 'picture' :
24552                 this.beforeSelectFile(e);
24553                 break;
24554             case 'trash' :
24555                 this.trash(e);
24556                 break;
24557             case 'crop' :
24558                 this.crop(e);
24559                 break;
24560             case 'download' :
24561                 this.download(e);
24562                 break;
24563             default :
24564                 break;
24565         }
24566         
24567         this.fireEvent('footerbuttonclick', this, type);
24568     },
24569     
24570     beforeSelectFile : function(e)
24571     {
24572         e.preventDefault();
24573         
24574         if(this.fireEvent('beforeselectfile', this) != false){
24575             this.selectorEl.dom.click();
24576         }
24577     },
24578     
24579     onFileSelected : function(e)
24580     {
24581         e.preventDefault();
24582         
24583         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24584             return;
24585         }
24586         
24587         var file = this.selectorEl.dom.files[0];
24588         
24589         if(this.fireEvent('inspect', this, file) != false){
24590             this.prepare(file);
24591         }
24592         
24593     },
24594     
24595     trash : function(e)
24596     {
24597         this.fireEvent('trash', this);
24598     },
24599     
24600     download : function(e)
24601     {
24602         this.fireEvent('download', this);
24603     },
24604     
24605     loadCanvas : function(src)
24606     {   
24607         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24608             
24609             this.reset();
24610             
24611             this.imageEl = document.createElement('img');
24612             
24613             var _this = this;
24614             
24615             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24616             
24617             this.imageEl.src = src;
24618         }
24619     },
24620     
24621     onLoadCanvas : function()
24622     {   
24623         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24624         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24625         
24626         this.bodyEl.un('click', this.beforeSelectFile, this);
24627         
24628         this.notifyEl.hide();
24629         this.thumbEl.show();
24630         this.footerEl.show();
24631         
24632         this.baseRotateLevel();
24633         
24634         if(this.isDocument){
24635             this.setThumbBoxSize();
24636         }
24637         
24638         this.setThumbBoxPosition();
24639         
24640         this.baseScaleLevel();
24641         
24642         this.draw();
24643         
24644         this.resize();
24645         
24646         this.canvasLoaded = true;
24647         
24648         if(this.loadMask){
24649             this.maskEl.unmask();
24650         }
24651         
24652     },
24653     
24654     setCanvasPosition : function()
24655     {   
24656         if(!this.canvasEl){
24657             return;
24658         }
24659         
24660         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24661         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24662         
24663         this.previewEl.setLeft(pw);
24664         this.previewEl.setTop(ph);
24665         
24666     },
24667     
24668     onMouseDown : function(e)
24669     {   
24670         e.stopEvent();
24671         
24672         this.dragable = true;
24673         this.pinching = false;
24674         
24675         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24676             this.dragable = false;
24677             return;
24678         }
24679         
24680         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24681         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24682         
24683     },
24684     
24685     onMouseMove : function(e)
24686     {   
24687         e.stopEvent();
24688         
24689         if(!this.canvasLoaded){
24690             return;
24691         }
24692         
24693         if (!this.dragable){
24694             return;
24695         }
24696         
24697         var minX = Math.ceil(this.thumbEl.getLeft(true));
24698         var minY = Math.ceil(this.thumbEl.getTop(true));
24699         
24700         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24701         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24702         
24703         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24704         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24705         
24706         x = x - this.mouseX;
24707         y = y - this.mouseY;
24708         
24709         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24710         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24711         
24712         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24713         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24714         
24715         this.previewEl.setLeft(bgX);
24716         this.previewEl.setTop(bgY);
24717         
24718         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24719         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24720     },
24721     
24722     onMouseUp : function(e)
24723     {   
24724         e.stopEvent();
24725         
24726         this.dragable = false;
24727     },
24728     
24729     onMouseWheel : function(e)
24730     {   
24731         e.stopEvent();
24732         
24733         this.startScale = this.scale;
24734         
24735         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24736         
24737         if(!this.zoomable()){
24738             this.scale = this.startScale;
24739             return;
24740         }
24741         
24742         this.draw();
24743         
24744         return;
24745     },
24746     
24747     zoomable : function()
24748     {
24749         var minScale = this.thumbEl.getWidth() / this.minWidth;
24750         
24751         if(this.minWidth < this.minHeight){
24752             minScale = this.thumbEl.getHeight() / this.minHeight;
24753         }
24754         
24755         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24756         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24757         
24758         if(
24759                 this.isDocument &&
24760                 (this.rotate == 0 || this.rotate == 180) && 
24761                 (
24762                     width > this.imageEl.OriginWidth || 
24763                     height > this.imageEl.OriginHeight ||
24764                     (width < this.minWidth && height < this.minHeight)
24765                 )
24766         ){
24767             return false;
24768         }
24769         
24770         if(
24771                 this.isDocument &&
24772                 (this.rotate == 90 || this.rotate == 270) && 
24773                 (
24774                     width > this.imageEl.OriginWidth || 
24775                     height > this.imageEl.OriginHeight ||
24776                     (width < this.minHeight && height < this.minWidth)
24777                 )
24778         ){
24779             return false;
24780         }
24781         
24782         if(
24783                 !this.isDocument &&
24784                 (this.rotate == 0 || this.rotate == 180) && 
24785                 (
24786                     width < this.minWidth || 
24787                     width > this.imageEl.OriginWidth || 
24788                     height < this.minHeight || 
24789                     height > this.imageEl.OriginHeight
24790                 )
24791         ){
24792             return false;
24793         }
24794         
24795         if(
24796                 !this.isDocument &&
24797                 (this.rotate == 90 || this.rotate == 270) && 
24798                 (
24799                     width < this.minHeight || 
24800                     width > this.imageEl.OriginWidth || 
24801                     height < this.minWidth || 
24802                     height > this.imageEl.OriginHeight
24803                 )
24804         ){
24805             return false;
24806         }
24807         
24808         return true;
24809         
24810     },
24811     
24812     onRotateLeft : function(e)
24813     {   
24814         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24815             
24816             var minScale = this.thumbEl.getWidth() / this.minWidth;
24817             
24818             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24819             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24820             
24821             this.startScale = this.scale;
24822             
24823             while (this.getScaleLevel() < minScale){
24824             
24825                 this.scale = this.scale + 1;
24826                 
24827                 if(!this.zoomable()){
24828                     break;
24829                 }
24830                 
24831                 if(
24832                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24833                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24834                 ){
24835                     continue;
24836                 }
24837                 
24838                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24839
24840                 this.draw();
24841                 
24842                 return;
24843             }
24844             
24845             this.scale = this.startScale;
24846             
24847             this.onRotateFail();
24848             
24849             return false;
24850         }
24851         
24852         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24853
24854         if(this.isDocument){
24855             this.setThumbBoxSize();
24856             this.setThumbBoxPosition();
24857             this.setCanvasPosition();
24858         }
24859         
24860         this.draw();
24861         
24862         this.fireEvent('rotate', this, 'left');
24863         
24864     },
24865     
24866     onRotateRight : function(e)
24867     {
24868         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24869             
24870             var minScale = this.thumbEl.getWidth() / this.minWidth;
24871         
24872             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24873             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24874             
24875             this.startScale = this.scale;
24876             
24877             while (this.getScaleLevel() < minScale){
24878             
24879                 this.scale = this.scale + 1;
24880                 
24881                 if(!this.zoomable()){
24882                     break;
24883                 }
24884                 
24885                 if(
24886                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24887                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24888                 ){
24889                     continue;
24890                 }
24891                 
24892                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24893
24894                 this.draw();
24895                 
24896                 return;
24897             }
24898             
24899             this.scale = this.startScale;
24900             
24901             this.onRotateFail();
24902             
24903             return false;
24904         }
24905         
24906         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24907
24908         if(this.isDocument){
24909             this.setThumbBoxSize();
24910             this.setThumbBoxPosition();
24911             this.setCanvasPosition();
24912         }
24913         
24914         this.draw();
24915         
24916         this.fireEvent('rotate', this, 'right');
24917     },
24918     
24919     onRotateFail : function()
24920     {
24921         this.errorEl.show(true);
24922         
24923         var _this = this;
24924         
24925         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24926     },
24927     
24928     draw : function()
24929     {
24930         this.previewEl.dom.innerHTML = '';
24931         
24932         var canvasEl = document.createElement("canvas");
24933         
24934         var contextEl = canvasEl.getContext("2d");
24935         
24936         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24937         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24938         var center = this.imageEl.OriginWidth / 2;
24939         
24940         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24941             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24942             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24943             center = this.imageEl.OriginHeight / 2;
24944         }
24945         
24946         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24947         
24948         contextEl.translate(center, center);
24949         contextEl.rotate(this.rotate * Math.PI / 180);
24950
24951         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24952         
24953         this.canvasEl = document.createElement("canvas");
24954         
24955         this.contextEl = this.canvasEl.getContext("2d");
24956         
24957         switch (this.rotate) {
24958             case 0 :
24959                 
24960                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24961                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24962                 
24963                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24964                 
24965                 break;
24966             case 90 : 
24967                 
24968                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24969                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24970                 
24971                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24972                     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);
24973                     break;
24974                 }
24975                 
24976                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24977                 
24978                 break;
24979             case 180 :
24980                 
24981                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24982                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24983                 
24984                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24985                     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);
24986                     break;
24987                 }
24988                 
24989                 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);
24990                 
24991                 break;
24992             case 270 :
24993                 
24994                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24995                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24996         
24997                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24998                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24999                     break;
25000                 }
25001                 
25002                 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);
25003                 
25004                 break;
25005             default : 
25006                 break;
25007         }
25008         
25009         this.previewEl.appendChild(this.canvasEl);
25010         
25011         this.setCanvasPosition();
25012     },
25013     
25014     crop : function()
25015     {
25016         if(!this.canvasLoaded){
25017             return;
25018         }
25019         
25020         var imageCanvas = document.createElement("canvas");
25021         
25022         var imageContext = imageCanvas.getContext("2d");
25023         
25024         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25025         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25026         
25027         var center = imageCanvas.width / 2;
25028         
25029         imageContext.translate(center, center);
25030         
25031         imageContext.rotate(this.rotate * Math.PI / 180);
25032         
25033         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25034         
25035         var canvas = document.createElement("canvas");
25036         
25037         var context = canvas.getContext("2d");
25038                 
25039         canvas.width = this.minWidth;
25040         canvas.height = this.minHeight;
25041
25042         switch (this.rotate) {
25043             case 0 :
25044                 
25045                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25046                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25047                 
25048                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25049                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25050                 
25051                 var targetWidth = this.minWidth - 2 * x;
25052                 var targetHeight = this.minHeight - 2 * y;
25053                 
25054                 var scale = 1;
25055                 
25056                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25057                     scale = targetWidth / width;
25058                 }
25059                 
25060                 if(x > 0 && y == 0){
25061                     scale = targetHeight / height;
25062                 }
25063                 
25064                 if(x > 0 && y > 0){
25065                     scale = targetWidth / width;
25066                     
25067                     if(width < height){
25068                         scale = targetHeight / height;
25069                     }
25070                 }
25071                 
25072                 context.scale(scale, scale);
25073                 
25074                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25075                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25076
25077                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25078                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25079
25080                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25081                 
25082                 break;
25083             case 90 : 
25084                 
25085                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25086                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25087                 
25088                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25089                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25090                 
25091                 var targetWidth = this.minWidth - 2 * x;
25092                 var targetHeight = this.minHeight - 2 * y;
25093                 
25094                 var scale = 1;
25095                 
25096                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25097                     scale = targetWidth / width;
25098                 }
25099                 
25100                 if(x > 0 && y == 0){
25101                     scale = targetHeight / height;
25102                 }
25103                 
25104                 if(x > 0 && y > 0){
25105                     scale = targetWidth / width;
25106                     
25107                     if(width < height){
25108                         scale = targetHeight / height;
25109                     }
25110                 }
25111                 
25112                 context.scale(scale, scale);
25113                 
25114                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25115                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25116
25117                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25118                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25119                 
25120                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25121                 
25122                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25123                 
25124                 break;
25125             case 180 :
25126                 
25127                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25128                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25129                 
25130                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25131                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25132                 
25133                 var targetWidth = this.minWidth - 2 * x;
25134                 var targetHeight = this.minHeight - 2 * y;
25135                 
25136                 var scale = 1;
25137                 
25138                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25139                     scale = targetWidth / width;
25140                 }
25141                 
25142                 if(x > 0 && y == 0){
25143                     scale = targetHeight / height;
25144                 }
25145                 
25146                 if(x > 0 && y > 0){
25147                     scale = targetWidth / width;
25148                     
25149                     if(width < height){
25150                         scale = targetHeight / height;
25151                     }
25152                 }
25153                 
25154                 context.scale(scale, scale);
25155                 
25156                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25157                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25158
25159                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25160                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25161
25162                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25163                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25164                 
25165                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25166                 
25167                 break;
25168             case 270 :
25169                 
25170                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25171                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25172                 
25173                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25174                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25175                 
25176                 var targetWidth = this.minWidth - 2 * x;
25177                 var targetHeight = this.minHeight - 2 * y;
25178                 
25179                 var scale = 1;
25180                 
25181                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25182                     scale = targetWidth / width;
25183                 }
25184                 
25185                 if(x > 0 && y == 0){
25186                     scale = targetHeight / height;
25187                 }
25188                 
25189                 if(x > 0 && y > 0){
25190                     scale = targetWidth / width;
25191                     
25192                     if(width < height){
25193                         scale = targetHeight / height;
25194                     }
25195                 }
25196                 
25197                 context.scale(scale, scale);
25198                 
25199                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25200                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25201
25202                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25203                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25204                 
25205                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25206                 
25207                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25208                 
25209                 break;
25210             default : 
25211                 break;
25212         }
25213         
25214         this.cropData = canvas.toDataURL(this.cropType);
25215         
25216         if(this.fireEvent('crop', this, this.cropData) !== false){
25217             this.process(this.file, this.cropData);
25218         }
25219         
25220         return;
25221         
25222     },
25223     
25224     setThumbBoxSize : function()
25225     {
25226         var width, height;
25227         
25228         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25229             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25230             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25231             
25232             this.minWidth = width;
25233             this.minHeight = height;
25234             
25235             if(this.rotate == 90 || this.rotate == 270){
25236                 this.minWidth = height;
25237                 this.minHeight = width;
25238             }
25239         }
25240         
25241         height = 300;
25242         width = Math.ceil(this.minWidth * height / this.minHeight);
25243         
25244         if(this.minWidth > this.minHeight){
25245             width = 300;
25246             height = Math.ceil(this.minHeight * width / this.minWidth);
25247         }
25248         
25249         this.thumbEl.setStyle({
25250             width : width + 'px',
25251             height : height + 'px'
25252         });
25253
25254         return;
25255             
25256     },
25257     
25258     setThumbBoxPosition : function()
25259     {
25260         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25261         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25262         
25263         this.thumbEl.setLeft(x);
25264         this.thumbEl.setTop(y);
25265         
25266     },
25267     
25268     baseRotateLevel : function()
25269     {
25270         this.baseRotate = 1;
25271         
25272         if(
25273                 typeof(this.exif) != 'undefined' &&
25274                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25275                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25276         ){
25277             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25278         }
25279         
25280         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25281         
25282     },
25283     
25284     baseScaleLevel : function()
25285     {
25286         var width, height;
25287         
25288         if(this.isDocument){
25289             
25290             if(this.baseRotate == 6 || this.baseRotate == 8){
25291             
25292                 height = this.thumbEl.getHeight();
25293                 this.baseScale = height / this.imageEl.OriginWidth;
25294
25295                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25296                     width = this.thumbEl.getWidth();
25297                     this.baseScale = width / this.imageEl.OriginHeight;
25298                 }
25299
25300                 return;
25301             }
25302
25303             height = this.thumbEl.getHeight();
25304             this.baseScale = height / this.imageEl.OriginHeight;
25305
25306             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25307                 width = this.thumbEl.getWidth();
25308                 this.baseScale = width / this.imageEl.OriginWidth;
25309             }
25310
25311             return;
25312         }
25313         
25314         if(this.baseRotate == 6 || this.baseRotate == 8){
25315             
25316             width = this.thumbEl.getHeight();
25317             this.baseScale = width / this.imageEl.OriginHeight;
25318             
25319             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25320                 height = this.thumbEl.getWidth();
25321                 this.baseScale = height / this.imageEl.OriginHeight;
25322             }
25323             
25324             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25325                 height = this.thumbEl.getWidth();
25326                 this.baseScale = height / this.imageEl.OriginHeight;
25327                 
25328                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25329                     width = this.thumbEl.getHeight();
25330                     this.baseScale = width / this.imageEl.OriginWidth;
25331                 }
25332             }
25333             
25334             return;
25335         }
25336         
25337         width = this.thumbEl.getWidth();
25338         this.baseScale = width / this.imageEl.OriginWidth;
25339         
25340         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25341             height = this.thumbEl.getHeight();
25342             this.baseScale = height / this.imageEl.OriginHeight;
25343         }
25344         
25345         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25346             
25347             height = this.thumbEl.getHeight();
25348             this.baseScale = height / this.imageEl.OriginHeight;
25349             
25350             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25351                 width = this.thumbEl.getWidth();
25352                 this.baseScale = width / this.imageEl.OriginWidth;
25353             }
25354             
25355         }
25356         
25357         return;
25358     },
25359     
25360     getScaleLevel : function()
25361     {
25362         return this.baseScale * Math.pow(1.1, this.scale);
25363     },
25364     
25365     onTouchStart : function(e)
25366     {
25367         if(!this.canvasLoaded){
25368             this.beforeSelectFile(e);
25369             return;
25370         }
25371         
25372         var touches = e.browserEvent.touches;
25373         
25374         if(!touches){
25375             return;
25376         }
25377         
25378         if(touches.length == 1){
25379             this.onMouseDown(e);
25380             return;
25381         }
25382         
25383         if(touches.length != 2){
25384             return;
25385         }
25386         
25387         var coords = [];
25388         
25389         for(var i = 0, finger; finger = touches[i]; i++){
25390             coords.push(finger.pageX, finger.pageY);
25391         }
25392         
25393         var x = Math.pow(coords[0] - coords[2], 2);
25394         var y = Math.pow(coords[1] - coords[3], 2);
25395         
25396         this.startDistance = Math.sqrt(x + y);
25397         
25398         this.startScale = this.scale;
25399         
25400         this.pinching = true;
25401         this.dragable = false;
25402         
25403     },
25404     
25405     onTouchMove : function(e)
25406     {
25407         if(!this.pinching && !this.dragable){
25408             return;
25409         }
25410         
25411         var touches = e.browserEvent.touches;
25412         
25413         if(!touches){
25414             return;
25415         }
25416         
25417         if(this.dragable){
25418             this.onMouseMove(e);
25419             return;
25420         }
25421         
25422         var coords = [];
25423         
25424         for(var i = 0, finger; finger = touches[i]; i++){
25425             coords.push(finger.pageX, finger.pageY);
25426         }
25427         
25428         var x = Math.pow(coords[0] - coords[2], 2);
25429         var y = Math.pow(coords[1] - coords[3], 2);
25430         
25431         this.endDistance = Math.sqrt(x + y);
25432         
25433         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25434         
25435         if(!this.zoomable()){
25436             this.scale = this.startScale;
25437             return;
25438         }
25439         
25440         this.draw();
25441         
25442     },
25443     
25444     onTouchEnd : function(e)
25445     {
25446         this.pinching = false;
25447         this.dragable = false;
25448         
25449     },
25450     
25451     process : function(file, crop)
25452     {
25453         if(this.loadMask){
25454             this.maskEl.mask(this.loadingText);
25455         }
25456         
25457         this.xhr = new XMLHttpRequest();
25458         
25459         file.xhr = this.xhr;
25460
25461         this.xhr.open(this.method, this.url, true);
25462         
25463         var headers = {
25464             "Accept": "application/json",
25465             "Cache-Control": "no-cache",
25466             "X-Requested-With": "XMLHttpRequest"
25467         };
25468         
25469         for (var headerName in headers) {
25470             var headerValue = headers[headerName];
25471             if (headerValue) {
25472                 this.xhr.setRequestHeader(headerName, headerValue);
25473             }
25474         }
25475         
25476         var _this = this;
25477         
25478         this.xhr.onload = function()
25479         {
25480             _this.xhrOnLoad(_this.xhr);
25481         }
25482         
25483         this.xhr.onerror = function()
25484         {
25485             _this.xhrOnError(_this.xhr);
25486         }
25487         
25488         var formData = new FormData();
25489
25490         formData.append('returnHTML', 'NO');
25491         
25492         if(crop){
25493             formData.append('crop', crop);
25494         }
25495         
25496         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25497             formData.append(this.paramName, file, file.name);
25498         }
25499         
25500         if(typeof(file.filename) != 'undefined'){
25501             formData.append('filename', file.filename);
25502         }
25503         
25504         if(typeof(file.mimetype) != 'undefined'){
25505             formData.append('mimetype', file.mimetype);
25506         }
25507         
25508         if(this.fireEvent('arrange', this, formData) != false){
25509             this.xhr.send(formData);
25510         };
25511     },
25512     
25513     xhrOnLoad : function(xhr)
25514     {
25515         if(this.loadMask){
25516             this.maskEl.unmask();
25517         }
25518         
25519         if (xhr.readyState !== 4) {
25520             this.fireEvent('exception', this, xhr);
25521             return;
25522         }
25523
25524         var response = Roo.decode(xhr.responseText);
25525         
25526         if(!response.success){
25527             this.fireEvent('exception', this, xhr);
25528             return;
25529         }
25530         
25531         var response = Roo.decode(xhr.responseText);
25532         
25533         this.fireEvent('upload', this, response);
25534         
25535     },
25536     
25537     xhrOnError : function()
25538     {
25539         if(this.loadMask){
25540             this.maskEl.unmask();
25541         }
25542         
25543         Roo.log('xhr on error');
25544         
25545         var response = Roo.decode(xhr.responseText);
25546           
25547         Roo.log(response);
25548         
25549     },
25550     
25551     prepare : function(file)
25552     {   
25553         if(this.loadMask){
25554             this.maskEl.mask(this.loadingText);
25555         }
25556         
25557         this.file = false;
25558         this.exif = {};
25559         
25560         if(typeof(file) === 'string'){
25561             this.loadCanvas(file);
25562             return;
25563         }
25564         
25565         if(!file || !this.urlAPI){
25566             return;
25567         }
25568         
25569         this.file = file;
25570         this.cropType = file.type;
25571         
25572         var _this = this;
25573         
25574         if(this.fireEvent('prepare', this, this.file) != false){
25575             
25576             var reader = new FileReader();
25577             
25578             reader.onload = function (e) {
25579                 if (e.target.error) {
25580                     Roo.log(e.target.error);
25581                     return;
25582                 }
25583                 
25584                 var buffer = e.target.result,
25585                     dataView = new DataView(buffer),
25586                     offset = 2,
25587                     maxOffset = dataView.byteLength - 4,
25588                     markerBytes,
25589                     markerLength;
25590                 
25591                 if (dataView.getUint16(0) === 0xffd8) {
25592                     while (offset < maxOffset) {
25593                         markerBytes = dataView.getUint16(offset);
25594                         
25595                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25596                             markerLength = dataView.getUint16(offset + 2) + 2;
25597                             if (offset + markerLength > dataView.byteLength) {
25598                                 Roo.log('Invalid meta data: Invalid segment size.');
25599                                 break;
25600                             }
25601                             
25602                             if(markerBytes == 0xffe1){
25603                                 _this.parseExifData(
25604                                     dataView,
25605                                     offset,
25606                                     markerLength
25607                                 );
25608                             }
25609                             
25610                             offset += markerLength;
25611                             
25612                             continue;
25613                         }
25614                         
25615                         break;
25616                     }
25617                     
25618                 }
25619                 
25620                 var url = _this.urlAPI.createObjectURL(_this.file);
25621                 
25622                 _this.loadCanvas(url);
25623                 
25624                 return;
25625             }
25626             
25627             reader.readAsArrayBuffer(this.file);
25628             
25629         }
25630         
25631     },
25632     
25633     parseExifData : function(dataView, offset, length)
25634     {
25635         var tiffOffset = offset + 10,
25636             littleEndian,
25637             dirOffset;
25638     
25639         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25640             // No Exif data, might be XMP data instead
25641             return;
25642         }
25643         
25644         // Check for the ASCII code for "Exif" (0x45786966):
25645         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25646             // No Exif data, might be XMP data instead
25647             return;
25648         }
25649         if (tiffOffset + 8 > dataView.byteLength) {
25650             Roo.log('Invalid Exif data: Invalid segment size.');
25651             return;
25652         }
25653         // Check for the two null bytes:
25654         if (dataView.getUint16(offset + 8) !== 0x0000) {
25655             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25656             return;
25657         }
25658         // Check the byte alignment:
25659         switch (dataView.getUint16(tiffOffset)) {
25660         case 0x4949:
25661             littleEndian = true;
25662             break;
25663         case 0x4D4D:
25664             littleEndian = false;
25665             break;
25666         default:
25667             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25668             return;
25669         }
25670         // Check for the TIFF tag marker (0x002A):
25671         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25672             Roo.log('Invalid Exif data: Missing TIFF marker.');
25673             return;
25674         }
25675         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25676         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25677         
25678         this.parseExifTags(
25679             dataView,
25680             tiffOffset,
25681             tiffOffset + dirOffset,
25682             littleEndian
25683         );
25684     },
25685     
25686     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25687     {
25688         var tagsNumber,
25689             dirEndOffset,
25690             i;
25691         if (dirOffset + 6 > dataView.byteLength) {
25692             Roo.log('Invalid Exif data: Invalid directory offset.');
25693             return;
25694         }
25695         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25696         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25697         if (dirEndOffset + 4 > dataView.byteLength) {
25698             Roo.log('Invalid Exif data: Invalid directory size.');
25699             return;
25700         }
25701         for (i = 0; i < tagsNumber; i += 1) {
25702             this.parseExifTag(
25703                 dataView,
25704                 tiffOffset,
25705                 dirOffset + 2 + 12 * i, // tag offset
25706                 littleEndian
25707             );
25708         }
25709         // Return the offset to the next directory:
25710         return dataView.getUint32(dirEndOffset, littleEndian);
25711     },
25712     
25713     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25714     {
25715         var tag = dataView.getUint16(offset, littleEndian);
25716         
25717         this.exif[tag] = this.getExifValue(
25718             dataView,
25719             tiffOffset,
25720             offset,
25721             dataView.getUint16(offset + 2, littleEndian), // tag type
25722             dataView.getUint32(offset + 4, littleEndian), // tag length
25723             littleEndian
25724         );
25725     },
25726     
25727     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25728     {
25729         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25730             tagSize,
25731             dataOffset,
25732             values,
25733             i,
25734             str,
25735             c;
25736     
25737         if (!tagType) {
25738             Roo.log('Invalid Exif data: Invalid tag type.');
25739             return;
25740         }
25741         
25742         tagSize = tagType.size * length;
25743         // Determine if the value is contained in the dataOffset bytes,
25744         // or if the value at the dataOffset is a pointer to the actual data:
25745         dataOffset = tagSize > 4 ?
25746                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25747         if (dataOffset + tagSize > dataView.byteLength) {
25748             Roo.log('Invalid Exif data: Invalid data offset.');
25749             return;
25750         }
25751         if (length === 1) {
25752             return tagType.getValue(dataView, dataOffset, littleEndian);
25753         }
25754         values = [];
25755         for (i = 0; i < length; i += 1) {
25756             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25757         }
25758         
25759         if (tagType.ascii) {
25760             str = '';
25761             // Concatenate the chars:
25762             for (i = 0; i < values.length; i += 1) {
25763                 c = values[i];
25764                 // Ignore the terminating NULL byte(s):
25765                 if (c === '\u0000') {
25766                     break;
25767                 }
25768                 str += c;
25769             }
25770             return str;
25771         }
25772         return values;
25773     }
25774     
25775 });
25776
25777 Roo.apply(Roo.bootstrap.UploadCropbox, {
25778     tags : {
25779         'Orientation': 0x0112
25780     },
25781     
25782     Orientation: {
25783             1: 0, //'top-left',
25784 //            2: 'top-right',
25785             3: 180, //'bottom-right',
25786 //            4: 'bottom-left',
25787 //            5: 'left-top',
25788             6: 90, //'right-top',
25789 //            7: 'right-bottom',
25790             8: 270 //'left-bottom'
25791     },
25792     
25793     exifTagTypes : {
25794         // byte, 8-bit unsigned int:
25795         1: {
25796             getValue: function (dataView, dataOffset) {
25797                 return dataView.getUint8(dataOffset);
25798             },
25799             size: 1
25800         },
25801         // ascii, 8-bit byte:
25802         2: {
25803             getValue: function (dataView, dataOffset) {
25804                 return String.fromCharCode(dataView.getUint8(dataOffset));
25805             },
25806             size: 1,
25807             ascii: true
25808         },
25809         // short, 16 bit int:
25810         3: {
25811             getValue: function (dataView, dataOffset, littleEndian) {
25812                 return dataView.getUint16(dataOffset, littleEndian);
25813             },
25814             size: 2
25815         },
25816         // long, 32 bit int:
25817         4: {
25818             getValue: function (dataView, dataOffset, littleEndian) {
25819                 return dataView.getUint32(dataOffset, littleEndian);
25820             },
25821             size: 4
25822         },
25823         // rational = two long values, first is numerator, second is denominator:
25824         5: {
25825             getValue: function (dataView, dataOffset, littleEndian) {
25826                 return dataView.getUint32(dataOffset, littleEndian) /
25827                     dataView.getUint32(dataOffset + 4, littleEndian);
25828             },
25829             size: 8
25830         },
25831         // slong, 32 bit signed int:
25832         9: {
25833             getValue: function (dataView, dataOffset, littleEndian) {
25834                 return dataView.getInt32(dataOffset, littleEndian);
25835             },
25836             size: 4
25837         },
25838         // srational, two slongs, first is numerator, second is denominator:
25839         10: {
25840             getValue: function (dataView, dataOffset, littleEndian) {
25841                 return dataView.getInt32(dataOffset, littleEndian) /
25842                     dataView.getInt32(dataOffset + 4, littleEndian);
25843             },
25844             size: 8
25845         }
25846     },
25847     
25848     footer : {
25849         STANDARD : [
25850             {
25851                 tag : 'div',
25852                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25853                 action : 'rotate-left',
25854                 cn : [
25855                     {
25856                         tag : 'button',
25857                         cls : 'btn btn-default',
25858                         html : '<i class="fa fa-undo"></i>'
25859                     }
25860                 ]
25861             },
25862             {
25863                 tag : 'div',
25864                 cls : 'btn-group roo-upload-cropbox-picture',
25865                 action : 'picture',
25866                 cn : [
25867                     {
25868                         tag : 'button',
25869                         cls : 'btn btn-default',
25870                         html : '<i class="fa fa-picture-o"></i>'
25871                     }
25872                 ]
25873             },
25874             {
25875                 tag : 'div',
25876                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25877                 action : 'rotate-right',
25878                 cn : [
25879                     {
25880                         tag : 'button',
25881                         cls : 'btn btn-default',
25882                         html : '<i class="fa fa-repeat"></i>'
25883                     }
25884                 ]
25885             }
25886         ],
25887         DOCUMENT : [
25888             {
25889                 tag : 'div',
25890                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25891                 action : 'rotate-left',
25892                 cn : [
25893                     {
25894                         tag : 'button',
25895                         cls : 'btn btn-default',
25896                         html : '<i class="fa fa-undo"></i>'
25897                     }
25898                 ]
25899             },
25900             {
25901                 tag : 'div',
25902                 cls : 'btn-group roo-upload-cropbox-download',
25903                 action : 'download',
25904                 cn : [
25905                     {
25906                         tag : 'button',
25907                         cls : 'btn btn-default',
25908                         html : '<i class="fa fa-download"></i>'
25909                     }
25910                 ]
25911             },
25912             {
25913                 tag : 'div',
25914                 cls : 'btn-group roo-upload-cropbox-crop',
25915                 action : 'crop',
25916                 cn : [
25917                     {
25918                         tag : 'button',
25919                         cls : 'btn btn-default',
25920                         html : '<i class="fa fa-crop"></i>'
25921                     }
25922                 ]
25923             },
25924             {
25925                 tag : 'div',
25926                 cls : 'btn-group roo-upload-cropbox-trash',
25927                 action : 'trash',
25928                 cn : [
25929                     {
25930                         tag : 'button',
25931                         cls : 'btn btn-default',
25932                         html : '<i class="fa fa-trash"></i>'
25933                     }
25934                 ]
25935             },
25936             {
25937                 tag : 'div',
25938                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25939                 action : 'rotate-right',
25940                 cn : [
25941                     {
25942                         tag : 'button',
25943                         cls : 'btn btn-default',
25944                         html : '<i class="fa fa-repeat"></i>'
25945                     }
25946                 ]
25947             }
25948         ],
25949         ROTATOR : [
25950             {
25951                 tag : 'div',
25952                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25953                 action : 'rotate-left',
25954                 cn : [
25955                     {
25956                         tag : 'button',
25957                         cls : 'btn btn-default',
25958                         html : '<i class="fa fa-undo"></i>'
25959                     }
25960                 ]
25961             },
25962             {
25963                 tag : 'div',
25964                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25965                 action : 'rotate-right',
25966                 cn : [
25967                     {
25968                         tag : 'button',
25969                         cls : 'btn btn-default',
25970                         html : '<i class="fa fa-repeat"></i>'
25971                     }
25972                 ]
25973             }
25974         ]
25975     }
25976 });
25977
25978 /*
25979 * Licence: LGPL
25980 */
25981
25982 /**
25983  * @class Roo.bootstrap.DocumentManager
25984  * @extends Roo.bootstrap.Component
25985  * Bootstrap DocumentManager class
25986  * @cfg {String} paramName default 'imageUpload'
25987  * @cfg {String} method default POST
25988  * @cfg {String} url action url
25989  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25990  * @cfg {Boolean} multiple multiple upload default true
25991  * @cfg {Number} thumbSize default 300
25992  * @cfg {String} fieldLabel
25993  * @cfg {Number} labelWidth default 4
25994  * @cfg {String} labelAlign (left|top) default left
25995  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25996  * 
25997  * @constructor
25998  * Create a new DocumentManager
25999  * @param {Object} config The config object
26000  */
26001
26002 Roo.bootstrap.DocumentManager = function(config){
26003     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26004     
26005     this.addEvents({
26006         /**
26007          * @event initial
26008          * Fire when initial the DocumentManager
26009          * @param {Roo.bootstrap.DocumentManager} this
26010          */
26011         "initial" : true,
26012         /**
26013          * @event inspect
26014          * inspect selected file
26015          * @param {Roo.bootstrap.DocumentManager} this
26016          * @param {File} file
26017          */
26018         "inspect" : true,
26019         /**
26020          * @event exception
26021          * Fire when xhr load exception
26022          * @param {Roo.bootstrap.DocumentManager} this
26023          * @param {XMLHttpRequest} xhr
26024          */
26025         "exception" : true,
26026         /**
26027          * @event prepare
26028          * prepare the form data
26029          * @param {Roo.bootstrap.DocumentManager} this
26030          * @param {Object} formData
26031          */
26032         "prepare" : true,
26033         /**
26034          * @event remove
26035          * Fire when remove the file
26036          * @param {Roo.bootstrap.DocumentManager} this
26037          * @param {Object} file
26038          */
26039         "remove" : true,
26040         /**
26041          * @event refresh
26042          * Fire after refresh the file
26043          * @param {Roo.bootstrap.DocumentManager} this
26044          */
26045         "refresh" : true,
26046         /**
26047          * @event click
26048          * Fire after click the image
26049          * @param {Roo.bootstrap.DocumentManager} this
26050          * @param {Object} file
26051          */
26052         "click" : true,
26053         /**
26054          * @event edit
26055          * Fire when upload a image and editable set to true
26056          * @param {Roo.bootstrap.DocumentManager} this
26057          * @param {Object} file
26058          */
26059         "edit" : true,
26060         /**
26061          * @event beforeselectfile
26062          * Fire before select file
26063          * @param {Roo.bootstrap.DocumentManager} this
26064          */
26065         "beforeselectfile" : true,
26066         /**
26067          * @event process
26068          * Fire before process file
26069          * @param {Roo.bootstrap.DocumentManager} this
26070          * @param {Object} file
26071          */
26072         "process" : true
26073         
26074     });
26075 };
26076
26077 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26078     
26079     boxes : 0,
26080     inputName : '',
26081     thumbSize : 300,
26082     multiple : true,
26083     files : [],
26084     method : 'POST',
26085     url : '',
26086     paramName : 'imageUpload',
26087     fieldLabel : '',
26088     labelWidth : 4,
26089     labelAlign : 'left',
26090     editable : true,
26091     delegates : [],
26092     
26093     getAutoCreate : function()
26094     {   
26095         var managerWidget = {
26096             tag : 'div',
26097             cls : 'roo-document-manager',
26098             cn : [
26099                 {
26100                     tag : 'input',
26101                     cls : 'roo-document-manager-selector',
26102                     type : 'file'
26103                 },
26104                 {
26105                     tag : 'div',
26106                     cls : 'roo-document-manager-uploader',
26107                     cn : [
26108                         {
26109                             tag : 'div',
26110                             cls : 'roo-document-manager-upload-btn',
26111                             html : '<i class="fa fa-plus"></i>'
26112                         }
26113                     ]
26114                     
26115                 }
26116             ]
26117         };
26118         
26119         var content = [
26120             {
26121                 tag : 'div',
26122                 cls : 'column col-md-12',
26123                 cn : managerWidget
26124             }
26125         ];
26126         
26127         if(this.fieldLabel.length){
26128             
26129             content = [
26130                 {
26131                     tag : 'div',
26132                     cls : 'column col-md-12',
26133                     html : this.fieldLabel
26134                 },
26135                 {
26136                     tag : 'div',
26137                     cls : 'column col-md-12',
26138                     cn : managerWidget
26139                 }
26140             ];
26141
26142             if(this.labelAlign == 'left'){
26143                 content = [
26144                     {
26145                         tag : 'div',
26146                         cls : 'column col-md-' + this.labelWidth,
26147                         html : this.fieldLabel
26148                     },
26149                     {
26150                         tag : 'div',
26151                         cls : 'column col-md-' + (12 - this.labelWidth),
26152                         cn : managerWidget
26153                     }
26154                 ];
26155                 
26156             }
26157         }
26158         
26159         var cfg = {
26160             tag : 'div',
26161             cls : 'row clearfix',
26162             cn : content
26163         };
26164         
26165         return cfg;
26166         
26167     },
26168     
26169     initEvents : function()
26170     {
26171         this.managerEl = this.el.select('.roo-document-manager', true).first();
26172         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26173         
26174         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26175         this.selectorEl.hide();
26176         
26177         if(this.multiple){
26178             this.selectorEl.attr('multiple', 'multiple');
26179         }
26180         
26181         this.selectorEl.on('change', this.onFileSelected, this);
26182         
26183         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26184         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26185         
26186         this.uploader.on('click', this.onUploaderClick, this);
26187         
26188         this.renderProgressDialog();
26189         
26190         var _this = this;
26191         
26192         window.addEventListener("resize", function() { _this.refresh(); } );
26193         
26194         this.fireEvent('initial', this);
26195     },
26196     
26197     renderProgressDialog : function()
26198     {
26199         var _this = this;
26200         
26201         this.progressDialog = new Roo.bootstrap.Modal({
26202             cls : 'roo-document-manager-progress-dialog',
26203             allow_close : false,
26204             title : '',
26205             buttons : [
26206                 {
26207                     name  :'cancel',
26208                     weight : 'danger',
26209                     html : 'Cancel'
26210                 }
26211             ], 
26212             listeners : { 
26213                 btnclick : function() {
26214                     _this.uploadCancel();
26215                     this.hide();
26216                 }
26217             }
26218         });
26219          
26220         this.progressDialog.render(Roo.get(document.body));
26221          
26222         this.progress = new Roo.bootstrap.Progress({
26223             cls : 'roo-document-manager-progress',
26224             active : true,
26225             striped : true
26226         });
26227         
26228         this.progress.render(this.progressDialog.getChildContainer());
26229         
26230         this.progressBar = new Roo.bootstrap.ProgressBar({
26231             cls : 'roo-document-manager-progress-bar',
26232             aria_valuenow : 0,
26233             aria_valuemin : 0,
26234             aria_valuemax : 12,
26235             panel : 'success'
26236         });
26237         
26238         this.progressBar.render(this.progress.getChildContainer());
26239     },
26240     
26241     onUploaderClick : function(e)
26242     {
26243         e.preventDefault();
26244      
26245         if(this.fireEvent('beforeselectfile', this) != false){
26246             this.selectorEl.dom.click();
26247         }
26248         
26249     },
26250     
26251     onFileSelected : function(e)
26252     {
26253         e.preventDefault();
26254         
26255         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26256             return;
26257         }
26258         
26259         Roo.each(this.selectorEl.dom.files, function(file){
26260             if(this.fireEvent('inspect', this, file) != false){
26261                 this.files.push(file);
26262             }
26263         }, this);
26264         
26265         this.queue();
26266         
26267     },
26268     
26269     queue : function()
26270     {
26271         this.selectorEl.dom.value = '';
26272         
26273         if(!this.files.length){
26274             return;
26275         }
26276         
26277         if(this.boxes > 0 && this.files.length > this.boxes){
26278             this.files = this.files.slice(0, this.boxes);
26279         }
26280         
26281         this.uploader.show();
26282         
26283         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26284             this.uploader.hide();
26285         }
26286         
26287         var _this = this;
26288         
26289         var files = [];
26290         
26291         var docs = [];
26292         
26293         Roo.each(this.files, function(file){
26294             
26295             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26296                 var f = this.renderPreview(file);
26297                 files.push(f);
26298                 return;
26299             }
26300             
26301             if(file.type.indexOf('image') != -1){
26302                 this.delegates.push(
26303                     (function(){
26304                         _this.process(file);
26305                     }).createDelegate(this)
26306                 );
26307         
26308                 return;
26309             }
26310             
26311             docs.push(
26312                 (function(){
26313                     _this.process(file);
26314                 }).createDelegate(this)
26315             );
26316             
26317         }, this);
26318         
26319         this.files = files;
26320         
26321         this.delegates = this.delegates.concat(docs);
26322         
26323         if(!this.delegates.length){
26324             this.refresh();
26325             return;
26326         }
26327         
26328         this.progressBar.aria_valuemax = this.delegates.length;
26329         
26330         this.arrange();
26331         
26332         return;
26333     },
26334     
26335     arrange : function()
26336     {
26337         if(!this.delegates.length){
26338             this.progressDialog.hide();
26339             this.refresh();
26340             return;
26341         }
26342         
26343         var delegate = this.delegates.shift();
26344         
26345         this.progressDialog.show();
26346         
26347         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26348         
26349         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26350         
26351         delegate();
26352     },
26353     
26354     refresh : function()
26355     {
26356         this.uploader.show();
26357         
26358         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26359             this.uploader.hide();
26360         }
26361         
26362         Roo.isTouch ? this.closable(false) : this.closable(true);
26363         
26364         this.fireEvent('refresh', this);
26365     },
26366     
26367     onRemove : function(e, el, o)
26368     {
26369         e.preventDefault();
26370         
26371         this.fireEvent('remove', this, o);
26372         
26373     },
26374     
26375     remove : function(o)
26376     {
26377         var files = [];
26378         
26379         Roo.each(this.files, function(file){
26380             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26381                 files.push(file);
26382                 return;
26383             }
26384
26385             o.target.remove();
26386
26387         }, this);
26388         
26389         this.files = files;
26390         
26391         this.refresh();
26392     },
26393     
26394     clear : function()
26395     {
26396         Roo.each(this.files, function(file){
26397             if(!file.target){
26398                 return;
26399             }
26400             
26401             file.target.remove();
26402
26403         }, this);
26404         
26405         this.files = [];
26406         
26407         this.refresh();
26408     },
26409     
26410     onClick : function(e, el, o)
26411     {
26412         e.preventDefault();
26413         
26414         this.fireEvent('click', this, o);
26415         
26416     },
26417     
26418     closable : function(closable)
26419     {
26420         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26421             
26422             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26423             
26424             if(closable){
26425                 el.show();
26426                 return;
26427             }
26428             
26429             el.hide();
26430             
26431         }, this);
26432     },
26433     
26434     xhrOnLoad : function(xhr)
26435     {
26436         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26437             el.remove();
26438         }, this);
26439         
26440         if (xhr.readyState !== 4) {
26441             this.arrange();
26442             this.fireEvent('exception', this, xhr);
26443             return;
26444         }
26445
26446         var response = Roo.decode(xhr.responseText);
26447         
26448         if(!response.success){
26449             this.arrange();
26450             this.fireEvent('exception', this, xhr);
26451             return;
26452         }
26453         
26454         var file = this.renderPreview(response.data);
26455         
26456         this.files.push(file);
26457         
26458         this.arrange();
26459         
26460     },
26461     
26462     xhrOnError : function()
26463     {
26464         Roo.log('xhr on error');
26465         
26466         var response = Roo.decode(xhr.responseText);
26467           
26468         Roo.log(response);
26469         
26470         this.arrange();
26471     },
26472     
26473     process : function(file)
26474     {
26475         if(this.fireEvent('process', this, file) !== false){
26476             if(this.editable && file.type.indexOf('image') != -1){
26477                 this.fireEvent('edit', this, file);
26478                 return;
26479             }
26480
26481             this.uploadStart(file, false);
26482
26483             return;
26484         }
26485         
26486     },
26487     
26488     uploadStart : function(file, crop)
26489     {
26490         this.xhr = new XMLHttpRequest();
26491         
26492         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26493             this.arrange();
26494             return;
26495         }
26496         
26497         file.xhr = this.xhr;
26498             
26499         this.managerEl.createChild({
26500             tag : 'div',
26501             cls : 'roo-document-manager-loading',
26502             cn : [
26503                 {
26504                     tag : 'div',
26505                     tooltip : file.name,
26506                     cls : 'roo-document-manager-thumb',
26507                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26508                 }
26509             ]
26510
26511         });
26512
26513         this.xhr.open(this.method, this.url, true);
26514         
26515         var headers = {
26516             "Accept": "application/json",
26517             "Cache-Control": "no-cache",
26518             "X-Requested-With": "XMLHttpRequest"
26519         };
26520         
26521         for (var headerName in headers) {
26522             var headerValue = headers[headerName];
26523             if (headerValue) {
26524                 this.xhr.setRequestHeader(headerName, headerValue);
26525             }
26526         }
26527         
26528         var _this = this;
26529         
26530         this.xhr.onload = function()
26531         {
26532             _this.xhrOnLoad(_this.xhr);
26533         }
26534         
26535         this.xhr.onerror = function()
26536         {
26537             _this.xhrOnError(_this.xhr);
26538         }
26539         
26540         var formData = new FormData();
26541
26542         formData.append('returnHTML', 'NO');
26543         
26544         if(crop){
26545             formData.append('crop', crop);
26546         }
26547         
26548         formData.append(this.paramName, file, file.name);
26549         
26550         if(this.fireEvent('prepare', this, formData) != false){
26551             this.xhr.send(formData);
26552         };
26553     },
26554     
26555     uploadCancel : function()
26556     {
26557         this.xhr.abort();
26558         
26559         this.delegates = [];
26560         
26561         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26562             el.remove();
26563         }, this);
26564         
26565         this.arrange();
26566     },
26567     
26568     renderPreview : function(file)
26569     {
26570         if(typeof(file.target) != 'undefined' && file.target){
26571             return file;
26572         }
26573         
26574         var previewEl = this.managerEl.createChild({
26575             tag : 'div',
26576             cls : 'roo-document-manager-preview',
26577             cn : [
26578                 {
26579                     tag : 'div',
26580                     tooltip : file.filename,
26581                     cls : 'roo-document-manager-thumb',
26582                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26583                 },
26584                 {
26585                     tag : 'button',
26586                     cls : 'close',
26587                     html : '<i class="fa fa-times-circle"></i>'
26588                 }
26589             ]
26590         });
26591
26592         var close = previewEl.select('button.close', true).first();
26593
26594         close.on('click', this.onRemove, this, file);
26595
26596         file.target = previewEl;
26597
26598         var image = previewEl.select('img', true).first();
26599         
26600         var _this = this;
26601         
26602         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26603         
26604         image.on('click', this.onClick, this, file);
26605         
26606         return file;
26607         
26608     },
26609     
26610     onPreviewLoad : function(file, image)
26611     {
26612         if(typeof(file.target) == 'undefined' || !file.target){
26613             return;
26614         }
26615         
26616         var width = image.dom.naturalWidth || image.dom.width;
26617         var height = image.dom.naturalHeight || image.dom.height;
26618         
26619         if(width > height){
26620             file.target.addClass('wide');
26621             return;
26622         }
26623         
26624         file.target.addClass('tall');
26625         return;
26626         
26627     },
26628     
26629     uploadFromSource : function(file, crop)
26630     {
26631         this.xhr = new XMLHttpRequest();
26632         
26633         this.managerEl.createChild({
26634             tag : 'div',
26635             cls : 'roo-document-manager-loading',
26636             cn : [
26637                 {
26638                     tag : 'div',
26639                     tooltip : file.name,
26640                     cls : 'roo-document-manager-thumb',
26641                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26642                 }
26643             ]
26644
26645         });
26646
26647         this.xhr.open(this.method, this.url, true);
26648         
26649         var headers = {
26650             "Accept": "application/json",
26651             "Cache-Control": "no-cache",
26652             "X-Requested-With": "XMLHttpRequest"
26653         };
26654         
26655         for (var headerName in headers) {
26656             var headerValue = headers[headerName];
26657             if (headerValue) {
26658                 this.xhr.setRequestHeader(headerName, headerValue);
26659             }
26660         }
26661         
26662         var _this = this;
26663         
26664         this.xhr.onload = function()
26665         {
26666             _this.xhrOnLoad(_this.xhr);
26667         }
26668         
26669         this.xhr.onerror = function()
26670         {
26671             _this.xhrOnError(_this.xhr);
26672         }
26673         
26674         var formData = new FormData();
26675
26676         formData.append('returnHTML', 'NO');
26677         
26678         formData.append('crop', crop);
26679         
26680         if(typeof(file.filename) != 'undefined'){
26681             formData.append('filename', file.filename);
26682         }
26683         
26684         if(typeof(file.mimetype) != 'undefined'){
26685             formData.append('mimetype', file.mimetype);
26686         }
26687         
26688         if(this.fireEvent('prepare', this, formData) != false){
26689             this.xhr.send(formData);
26690         };
26691     }
26692 });
26693
26694 /*
26695 * Licence: LGPL
26696 */
26697
26698 /**
26699  * @class Roo.bootstrap.DocumentViewer
26700  * @extends Roo.bootstrap.Component
26701  * Bootstrap DocumentViewer class
26702  * 
26703  * @constructor
26704  * Create a new DocumentViewer
26705  * @param {Object} config The config object
26706  */
26707
26708 Roo.bootstrap.DocumentViewer = function(config){
26709     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26710     
26711     this.addEvents({
26712         /**
26713          * @event initial
26714          * Fire after initEvent
26715          * @param {Roo.bootstrap.DocumentViewer} this
26716          */
26717         "initial" : true,
26718         /**
26719          * @event click
26720          * Fire after click
26721          * @param {Roo.bootstrap.DocumentViewer} this
26722          */
26723         "click" : true,
26724         /**
26725          * @event trash
26726          * Fire after trash button
26727          * @param {Roo.bootstrap.DocumentViewer} this
26728          */
26729         "trash" : true
26730         
26731     });
26732 };
26733
26734 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26735     
26736     getAutoCreate : function()
26737     {
26738         var cfg = {
26739             tag : 'div',
26740             cls : 'roo-document-viewer',
26741             cn : [
26742                 {
26743                     tag : 'div',
26744                     cls : 'roo-document-viewer-body',
26745                     cn : [
26746                         {
26747                             tag : 'div',
26748                             cls : 'roo-document-viewer-thumb',
26749                             cn : [
26750                                 {
26751                                     tag : 'img',
26752                                     cls : 'roo-document-viewer-image'
26753                                 }
26754                             ]
26755                         }
26756                     ]
26757                 },
26758                 {
26759                     tag : 'div',
26760                     cls : 'roo-document-viewer-footer',
26761                     cn : {
26762                         tag : 'div',
26763                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26764                         cn : [
26765                             {
26766                                 tag : 'div',
26767                                 cls : 'btn-group',
26768                                 cn : [
26769                                     {
26770                                         tag : 'button',
26771                                         cls : 'btn btn-default roo-document-viewer-trash',
26772                                         html : '<i class="fa fa-trash"></i>'
26773                                     }
26774                                 ]
26775                             }
26776                         ]
26777                     }
26778                 }
26779             ]
26780         };
26781         
26782         return cfg;
26783     },
26784     
26785     initEvents : function()
26786     {
26787         
26788         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26789         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26790         
26791         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26792         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26793         
26794         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26795         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26796         
26797         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26798         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26799         
26800         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26801         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26802         
26803         this.bodyEl.on('click', this.onClick, this);
26804         
26805         this.trashBtn.on('click', this.onTrash, this);
26806         
26807     },
26808     
26809     initial : function()
26810     {
26811 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26812         
26813         
26814         this.fireEvent('initial', this);
26815         
26816     },
26817     
26818     onClick : function(e)
26819     {
26820         e.preventDefault();
26821         
26822         this.fireEvent('click', this);
26823     },
26824     
26825     onTrash : function(e)
26826     {
26827         e.preventDefault();
26828         
26829         this.fireEvent('trash', this);
26830     }
26831     
26832 });
26833 /*
26834  * - LGPL
26835  *
26836  * nav progress bar
26837  * 
26838  */
26839
26840 /**
26841  * @class Roo.bootstrap.NavProgressBar
26842  * @extends Roo.bootstrap.Component
26843  * Bootstrap NavProgressBar class
26844  * 
26845  * @constructor
26846  * Create a new nav progress bar
26847  * @param {Object} config The config object
26848  */
26849
26850 Roo.bootstrap.NavProgressBar = function(config){
26851     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26852
26853     this.bullets = this.bullets || [];
26854    
26855 //    Roo.bootstrap.NavProgressBar.register(this);
26856      this.addEvents({
26857         /**
26858              * @event changed
26859              * Fires when the active item changes
26860              * @param {Roo.bootstrap.NavProgressBar} this
26861              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26862              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26863          */
26864         'changed': true
26865      });
26866     
26867 };
26868
26869 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26870     
26871     bullets : [],
26872     barItems : [],
26873     
26874     getAutoCreate : function()
26875     {
26876         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26877         
26878         cfg = {
26879             tag : 'div',
26880             cls : 'roo-navigation-bar-group',
26881             cn : [
26882                 {
26883                     tag : 'div',
26884                     cls : 'roo-navigation-top-bar'
26885                 },
26886                 {
26887                     tag : 'div',
26888                     cls : 'roo-navigation-bullets-bar',
26889                     cn : [
26890                         {
26891                             tag : 'ul',
26892                             cls : 'roo-navigation-bar'
26893                         }
26894                     ]
26895                 },
26896                 
26897                 {
26898                     tag : 'div',
26899                     cls : 'roo-navigation-bottom-bar'
26900                 }
26901             ]
26902             
26903         };
26904         
26905         return cfg;
26906         
26907     },
26908     
26909     initEvents: function() 
26910     {
26911         
26912     },
26913     
26914     onRender : function(ct, position) 
26915     {
26916         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26917         
26918         if(this.bullets.length){
26919             Roo.each(this.bullets, function(b){
26920                this.addItem(b);
26921             }, this);
26922         }
26923         
26924         this.format();
26925         
26926     },
26927     
26928     addItem : function(cfg)
26929     {
26930         var item = new Roo.bootstrap.NavProgressItem(cfg);
26931         
26932         item.parentId = this.id;
26933         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26934         
26935         if(cfg.html){
26936             var top = new Roo.bootstrap.Element({
26937                 tag : 'div',
26938                 cls : 'roo-navigation-bar-text'
26939             });
26940             
26941             var bottom = new Roo.bootstrap.Element({
26942                 tag : 'div',
26943                 cls : 'roo-navigation-bar-text'
26944             });
26945             
26946             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26947             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26948             
26949             var topText = new Roo.bootstrap.Element({
26950                 tag : 'span',
26951                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26952             });
26953             
26954             var bottomText = new Roo.bootstrap.Element({
26955                 tag : 'span',
26956                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26957             });
26958             
26959             topText.onRender(top.el, null);
26960             bottomText.onRender(bottom.el, null);
26961             
26962             item.topEl = top;
26963             item.bottomEl = bottom;
26964         }
26965         
26966         this.barItems.push(item);
26967         
26968         return item;
26969     },
26970     
26971     getActive : function()
26972     {
26973         var active = false;
26974         
26975         Roo.each(this.barItems, function(v){
26976             
26977             if (!v.isActive()) {
26978                 return;
26979             }
26980             
26981             active = v;
26982             return false;
26983             
26984         });
26985         
26986         return active;
26987     },
26988     
26989     setActiveItem : function(item)
26990     {
26991         var prev = false;
26992         
26993         Roo.each(this.barItems, function(v){
26994             if (v.rid == item.rid) {
26995                 return ;
26996             }
26997             
26998             if (v.isActive()) {
26999                 v.setActive(false);
27000                 prev = v;
27001             }
27002         });
27003
27004         item.setActive(true);
27005         
27006         this.fireEvent('changed', this, item, prev);
27007     },
27008     
27009     getBarItem: function(rid)
27010     {
27011         var ret = false;
27012         
27013         Roo.each(this.barItems, function(e) {
27014             if (e.rid != rid) {
27015                 return;
27016             }
27017             
27018             ret =  e;
27019             return false;
27020         });
27021         
27022         return ret;
27023     },
27024     
27025     indexOfItem : function(item)
27026     {
27027         var index = false;
27028         
27029         Roo.each(this.barItems, function(v, i){
27030             
27031             if (v.rid != item.rid) {
27032                 return;
27033             }
27034             
27035             index = i;
27036             return false
27037         });
27038         
27039         return index;
27040     },
27041     
27042     setActiveNext : function()
27043     {
27044         var i = this.indexOfItem(this.getActive());
27045         
27046         if (i > this.barItems.length) {
27047             return;
27048         }
27049         
27050         this.setActiveItem(this.barItems[i+1]);
27051     },
27052     
27053     setActivePrev : function()
27054     {
27055         var i = this.indexOfItem(this.getActive());
27056         
27057         if (i  < 1) {
27058             return;
27059         }
27060         
27061         this.setActiveItem(this.barItems[i-1]);
27062     },
27063     
27064     format : function()
27065     {
27066         if(!this.barItems.length){
27067             return;
27068         }
27069      
27070         var width = 100 / this.barItems.length;
27071         
27072         Roo.each(this.barItems, function(i){
27073             i.el.setStyle('width', width + '%');
27074             i.topEl.el.setStyle('width', width + '%');
27075             i.bottomEl.el.setStyle('width', width + '%');
27076         }, this);
27077         
27078     }
27079     
27080 });
27081 /*
27082  * - LGPL
27083  *
27084  * Nav Progress Item
27085  * 
27086  */
27087
27088 /**
27089  * @class Roo.bootstrap.NavProgressItem
27090  * @extends Roo.bootstrap.Component
27091  * Bootstrap NavProgressItem class
27092  * @cfg {String} rid the reference id
27093  * @cfg {Boolean} active (true|false) Is item active default false
27094  * @cfg {Boolean} disabled (true|false) Is item active default false
27095  * @cfg {String} html
27096  * @cfg {String} position (top|bottom) text position default bottom
27097  * @cfg {String} icon show icon instead of number
27098  * 
27099  * @constructor
27100  * Create a new NavProgressItem
27101  * @param {Object} config The config object
27102  */
27103 Roo.bootstrap.NavProgressItem = function(config){
27104     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27105     this.addEvents({
27106         // raw events
27107         /**
27108          * @event click
27109          * The raw click event for the entire grid.
27110          * @param {Roo.bootstrap.NavProgressItem} this
27111          * @param {Roo.EventObject} e
27112          */
27113         "click" : true
27114     });
27115    
27116 };
27117
27118 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27119     
27120     rid : '',
27121     active : false,
27122     disabled : false,
27123     html : '',
27124     position : 'bottom',
27125     icon : false,
27126     
27127     getAutoCreate : function()
27128     {
27129         var iconCls = 'roo-navigation-bar-item-icon';
27130         
27131         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27132         
27133         var cfg = {
27134             tag: 'li',
27135             cls: 'roo-navigation-bar-item',
27136             cn : [
27137                 {
27138                     tag : 'i',
27139                     cls : iconCls
27140                 }
27141             ]
27142         }
27143         
27144         if(this.active){
27145             cfg.cls += ' active';
27146         }
27147         if(this.disabled){
27148             cfg.cls += ' disabled';
27149         }
27150         
27151         return cfg;
27152     },
27153     
27154     disable : function()
27155     {
27156         this.setDisabled(true);
27157     },
27158     
27159     enable : function()
27160     {
27161         this.setDisabled(false);
27162     },
27163     
27164     initEvents: function() 
27165     {
27166         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27167         
27168         this.iconEl.on('click', this.onClick, this);
27169     },
27170     
27171     onClick : function(e)
27172     {
27173         e.preventDefault();
27174         
27175         if(this.disabled){
27176             return;
27177         }
27178         
27179         if(this.fireEvent('click', this, e) === false){
27180             return;
27181         };
27182         
27183         this.parent().setActiveItem(this);
27184     },
27185     
27186     isActive: function () 
27187     {
27188         return this.active;
27189     },
27190     
27191     setActive : function(state)
27192     {
27193         if(this.active == state){
27194             return;
27195         }
27196         
27197         this.active = state;
27198         
27199         if (state) {
27200             this.el.addClass('active');
27201             return;
27202         }
27203         
27204         this.el.removeClass('active');
27205         
27206         return;
27207     },
27208     
27209     setDisabled : function(state)
27210     {
27211         if(this.disabled == state){
27212             return;
27213         }
27214         
27215         this.disabled = state;
27216         
27217         if (state) {
27218             this.el.addClass('disabled');
27219             return;
27220         }
27221         
27222         this.el.removeClass('disabled');
27223     },
27224     
27225     tooltipEl : function()
27226     {
27227         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27228     }
27229 });
27230  
27231
27232