docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         };
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             };
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         };
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             };
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 };
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         };
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             };
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         };
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         
1969         
1970         
1971         
1972         if (Roo.isTouch) {
1973             this.el.on('touchstart'  , this.onTouch, this);
1974         }
1975         this.el.on('click' , this.onClick, this);
1976
1977         this.el.on("mouseover", this.onMouseOver, this);
1978         this.el.on("mouseout", this.onMouseOut, this);
1979         
1980         
1981     },
1982     findTargetItem : function(e){
1983         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1984         if(!t){
1985             return false;
1986         }
1987         //Roo.log(t);         Roo.log(t.id);
1988         if(t && t.id){
1989             //Roo.log(this.menuitems);
1990             return this.menuitems.get(t.id);
1991             
1992             //return this.items.get(t.menuItemId);
1993         }
1994         
1995         return false;
1996     },
1997     
1998     onTouch : function(e) {
1999         e.stopEvent();
2000         this.onClick(e);
2001     },
2002     
2003     onClick : function(e){
2004         Roo.log("menu.onClick");
2005         var t = this.findTargetItem(e);
2006         if(!t || t.isContainer){
2007             return;
2008         }
2009         Roo.log(e);
2010         /*
2011         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2012             if(t == this.activeItem && t.shouldDeactivate(e)){
2013                 this.activeItem.deactivate();
2014                 delete this.activeItem;
2015                 return;
2016             }
2017             if(t.canActivate){
2018                 this.setActiveItem(t, true);
2019             }
2020             return;
2021             
2022             
2023         }
2024         */
2025        
2026         Roo.log('pass click event');
2027         
2028         t.onClick(e);
2029         
2030         this.fireEvent("click", this, t, e);
2031         
2032         this.hide();
2033     },
2034      onMouseOver : function(e){
2035         var t  = this.findTargetItem(e);
2036         //Roo.log(t);
2037         //if(t){
2038         //    if(t.canActivate && !t.disabled){
2039         //        this.setActiveItem(t, true);
2040         //    }
2041         //}
2042         
2043         this.fireEvent("mouseover", this, e, t);
2044     },
2045     isVisible : function(){
2046         return !this.hidden;
2047     },
2048      onMouseOut : function(e){
2049         var t  = this.findTargetItem(e);
2050         
2051         //if(t ){
2052         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2053         //        this.activeItem.deactivate();
2054         //        delete this.activeItem;
2055         //    }
2056         //}
2057         this.fireEvent("mouseout", this, e, t);
2058     },
2059     
2060     
2061     /**
2062      * Displays this menu relative to another element
2063      * @param {String/HTMLElement/Roo.Element} element The element to align to
2064      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065      * the element (defaults to this.defaultAlign)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     show : function(el, pos, parentMenu){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         this.fireEvent("beforeshow", this);
2074         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2075     },
2076      /**
2077      * Displays this menu at a specific xy position
2078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2080      */
2081     showAt : function(xy, parentMenu, /* private: */_e){
2082         this.parentMenu = parentMenu;
2083         if(!this.el){
2084             this.render();
2085         }
2086         if(_e !== false){
2087             this.fireEvent("beforeshow", this);
2088             //xy = this.el.adjustForConstraints(xy);
2089         }
2090         
2091         //this.el.show();
2092         this.hideMenuItems();
2093         this.hidden = false;
2094         this.triggerEl.addClass('open');
2095         
2096         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2098         }
2099         
2100         this.el.setXY(xy);
2101         this.focus();
2102         this.fireEvent("show", this);
2103     },
2104     
2105     focus : function(){
2106         return;
2107         if(!this.hidden){
2108             this.doFocus.defer(50, this);
2109         }
2110     },
2111
2112     doFocus : function(){
2113         if(!this.hidden){
2114             this.focusEl.focus();
2115         }
2116     },
2117
2118     /**
2119      * Hides this menu and optionally all parent menus
2120      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2121      */
2122     hide : function(deep){
2123         
2124         this.hideMenuItems();
2125         if(this.el && this.isVisible()){
2126             this.fireEvent("beforehide", this);
2127             if(this.activeItem){
2128                 this.activeItem.deactivate();
2129                 this.activeItem = null;
2130             }
2131             this.triggerEl.removeClass('open');;
2132             this.hidden = true;
2133             this.fireEvent("hide", this);
2134         }
2135         if(deep === true && this.parentMenu){
2136             this.parentMenu.hide(true);
2137         }
2138     },
2139     
2140     onTriggerPress  : function(e)
2141     {
2142         
2143         Roo.log('trigger press');
2144         //Roo.log(e.getTarget());
2145        // Roo.log(this.triggerEl.dom);
2146         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2147             return;
2148         }
2149         
2150         if (this.isVisible()) {
2151             Roo.log('hide');
2152             this.hide();
2153         } else {
2154             Roo.log('show');
2155             this.show(this.triggerEl, false, false);
2156         }
2157         
2158         e.stopEvent();
2159     },
2160     
2161          
2162        
2163     
2164     hideMenuItems : function()
2165     {
2166         //$(backdrop).remove()
2167         Roo.select('.open',true).each(function(aa) {
2168             
2169             aa.removeClass('open');
2170           //var parent = getParent($(this))
2171           //var relatedTarget = { relatedTarget: this }
2172           
2173            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174           //if (e.isDefaultPrevented()) return
2175            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2176         })
2177     },
2178     addxtypeChild : function (tree, cntr) {
2179         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2180           
2181         this.menuitems.add(comp);
2182         return comp;
2183
2184     },
2185     getEl : function()
2186     {
2187         Roo.log(this.el);
2188         return this.el;
2189     }
2190 });
2191
2192  
2193  /*
2194  * - LGPL
2195  *
2196  * menu item
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuItem
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuItem class
2205  * @cfg {String} html the menu label
2206  * @cfg {String} href the link
2207  * @cfg {Boolean} preventDefault (true | false) default true
2208  * @cfg {Boolean} isContainer (true | false) default false
2209  * 
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuItem = function(config){
2218     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2219     this.addEvents({
2220         // raw events
2221         /**
2222          * @event click
2223          * The raw click event for the entire grid.
2224          * @param {Roo.bootstrap.MenuItem} this
2225          * @param {Roo.EventObject} e
2226          */
2227         "click" : true
2228     });
2229 };
2230
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2232     
2233     href : false,
2234     html : false,
2235     preventDefault: true,
2236     isContainer : false,
2237     
2238     getAutoCreate : function(){
2239         
2240         if(this.isContainer){
2241             return {
2242                 tag: 'li',
2243                 cls: 'dropdown-menu-item'
2244             };
2245         }
2246         
2247         var cfg= {
2248             tag: 'li',
2249             cls: 'dropdown-menu-item',
2250             cn: [
2251                     {
2252                         tag : 'a',
2253                         href : '#',
2254                         html : 'Link'
2255                     }
2256                 ]
2257         };
2258         if (this.parent().type == 'treeview') {
2259             cfg.cls = 'treeview-menu';
2260         }
2261         
2262         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2264         return cfg;
2265     },
2266     
2267     initEvents: function() {
2268         
2269         //this.el.select('a').on('click', this.onClick, this);
2270         
2271     },
2272     onClick : function(e)
2273     {
2274         Roo.log('item on click ');
2275         //if(this.preventDefault){
2276         //    e.preventDefault();
2277         //}
2278         //this.parent().hideMenuItems();
2279         
2280         this.fireEvent('click', this, e);
2281     },
2282     getEl : function()
2283     {
2284         return this.el;
2285     }
2286 });
2287
2288  
2289
2290  /*
2291  * - LGPL
2292  *
2293  * menu separator
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuSeparator
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuSeparator class
2302  * 
2303  * @constructor
2304  * Create a new MenuItem
2305  * @param {Object} config The config object
2306  */
2307
2308
2309 Roo.bootstrap.MenuSeparator = function(config){
2310     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2311 };
2312
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2314     
2315     getAutoCreate : function(){
2316         var cfg = {
2317             cls: 'divider',
2318             tag : 'li'
2319         };
2320         
2321         return cfg;
2322     }
2323    
2324 });
2325
2326  
2327
2328  
2329 /*
2330 * Licence: LGPL
2331 */
2332
2333 /**
2334  * @class Roo.bootstrap.Modal
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Modal class
2337  * @cfg {String} title Title of dialog
2338  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2340  * @cfg {Boolean} specificTitle default false
2341  * @cfg {Array} buttons Array of buttons or standard button set..
2342  * @cfg {String} buttonPosition (left|right|center) default right
2343  * @cfg {Boolean} animate default true
2344  * @cfg {Boolean} allow_close default true
2345  * 
2346  * @constructor
2347  * Create a new Modal Dialog
2348  * @param {Object} config The config object
2349  */
2350
2351 Roo.bootstrap.Modal = function(config){
2352     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2353     this.addEvents({
2354         // raw events
2355         /**
2356          * @event btnclick
2357          * The raw btnclick event for the button
2358          * @param {Roo.EventObject} e
2359          */
2360         "btnclick" : true
2361     });
2362     this.buttons = this.buttons || [];
2363      
2364     if (this.tmpl) {
2365         this.tmpl = Roo.factory(this.tmpl);
2366     }
2367     
2368 };
2369
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2371     
2372     title : 'test dialog',
2373    
2374     buttons : false,
2375     
2376     // set on load...
2377      
2378     html: false,
2379     
2380     tmp: false,
2381     
2382     specificTitle: false,
2383     
2384     buttonPosition: 'right',
2385     
2386     allow_close : true,
2387     
2388     animate : true,
2389     
2390     
2391      // private
2392     bodyEl:  false,
2393     footerEl:  false,
2394     titleEl:  false,
2395     closeEl:  false,
2396     
2397     
2398     onRender : function(ct, position)
2399     {
2400         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2401      
2402         if(!this.el){
2403             var cfg = Roo.apply({},  this.getAutoCreate());
2404             cfg.id = Roo.id();
2405             //if(!cfg.name){
2406             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2407             //}
2408             //if (!cfg.name.length) {
2409             //    delete cfg.name;
2410            // }
2411             if (this.cls) {
2412                 cfg.cls += ' ' + this.cls;
2413             }
2414             if (this.style) {
2415                 cfg.style = this.style;
2416             }
2417             this.el = Roo.get(document.body).createChild(cfg, position);
2418         }
2419         //var type = this.el.dom.type;
2420         
2421         
2422         
2423         
2424         if(this.tabIndex !== undefined){
2425             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2426         }
2427         
2428         
2429         this.bodyEl = this.el.select('.modal-body',true).first();
2430         this.closeEl = this.el.select('.modal-header .close', true).first();
2431         this.footerEl = this.el.select('.modal-footer',true).first();
2432         this.titleEl = this.el.select('.modal-title',true).first();
2433         
2434         
2435          
2436         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437         this.maskEl.enableDisplayMode("block");
2438         this.maskEl.hide();
2439         //this.el.addClass("x-dlg-modal");
2440     
2441         if (this.buttons.length) {
2442             Roo.each(this.buttons, function(bb) {
2443                 var b = Roo.apply({}, bb);
2444                 b.xns = b.xns || Roo.bootstrap;
2445                 b.xtype = b.xtype || 'Button';
2446                 if (typeof(b.listeners) == 'undefined') {
2447                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2448                 }
2449                 
2450                 var btn = Roo.factory(b);
2451                 
2452                 btn.onRender(this.el.select('.modal-footer div').first());
2453                 
2454             },this);
2455         }
2456         // render the children.
2457         var nitems = [];
2458         
2459         if(typeof(this.items) != 'undefined'){
2460             var items = this.items;
2461             delete this.items;
2462
2463             for(var i =0;i < items.length;i++) {
2464                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2465             }
2466         }
2467         
2468         this.items = nitems;
2469         
2470         // where are these used - they used to be body/close/footer
2471         
2472        
2473         this.initEvents();
2474         //this.el.addClass([this.fieldClass, this.cls]);
2475         
2476     },
2477     
2478     getAutoCreate : function(){
2479         
2480         
2481         var bdy = {
2482                 cls : 'modal-body',
2483                 html : this.html || ''
2484         };
2485         
2486         var title = {
2487             tag: 'h4',
2488             cls : 'modal-title',
2489             html : this.title
2490         };
2491         
2492         if(this.specificTitle){
2493             title = this.title;
2494             
2495         };
2496         
2497         var header = [];
2498         if (this.allow_close) {
2499             header.push({
2500                 tag: 'button',
2501                 cls : 'close',
2502                 html : '&times'
2503             });
2504         }
2505         header.push(title);
2506         
2507         var modal = {
2508             cls: "modal",
2509             style : 'display: none',
2510             cn : [
2511                 {
2512                     cls: "modal-dialog",
2513                     cn : [
2514                         {
2515                             cls : "modal-content",
2516                             cn : [
2517                                 {
2518                                     cls : 'modal-header',
2519                                     cn : header
2520                                 },
2521                                 bdy,
2522                                 {
2523                                     cls : 'modal-footer',
2524                                     cn : [
2525                                         {
2526                                             tag: 'div',
2527                                             cls: 'btn-' + this.buttonPosition
2528                                         }
2529                                     ]
2530                                     
2531                                 }
2532                                 
2533                                 
2534                             ]
2535                             
2536                         }
2537                     ]
2538                         
2539                 }
2540             ]
2541         };
2542         
2543         if(this.animate){
2544             modal.cls += ' fade';
2545         }
2546         
2547         return modal;
2548           
2549     },
2550     getChildContainer : function() {
2551          
2552          return this.bodyEl;
2553         
2554     },
2555     getButtonContainer : function() {
2556          return this.el.select('.modal-footer div',true).first();
2557         
2558     },
2559     initEvents : function()
2560     {
2561         if (this.allow_close) {
2562             this.closeEl.on('click', this.hide, this);
2563         }
2564         
2565         var _this = this;
2566         
2567         window.addEventListener("resize", function() { _this.resize(); } );
2568
2569     },
2570     
2571     resize : function()
2572     {
2573         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2574     },
2575     
2576     show : function() {
2577         
2578         if (!this.rendered) {
2579             this.render();
2580         }
2581         
2582         this.el.setStyle('display', 'block');
2583         
2584         if(this.animate){
2585             var _this = this;
2586             (function(){ _this.el.addClass('in'); }).defer(50);
2587         }else{
2588             this.el.addClass('in');
2589         }
2590         
2591         // not sure how we can show data in here.. 
2592         //if (this.tmpl) {
2593         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2594         //}
2595         
2596         Roo.get(document.body).addClass("x-body-masked");
2597         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2598         this.maskEl.show();
2599         this.el.setStyle('zIndex', '10001');
2600        
2601         this.fireEvent('show', this);
2602         
2603         
2604     },
2605     hide : function()
2606     {
2607         this.maskEl.hide();
2608         Roo.get(document.body).removeClass("x-body-masked");
2609         this.el.removeClass('in');
2610         
2611         if(this.animate){
2612             var _this = this;
2613             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2614         }else{
2615             this.el.setStyle('display', 'none');
2616         }
2617         
2618         this.fireEvent('hide', this);
2619     },
2620     
2621     addButton : function(str, cb)
2622     {
2623          
2624         
2625         var b = Roo.apply({}, { html : str } );
2626         b.xns = b.xns || Roo.bootstrap;
2627         b.xtype = b.xtype || 'Button';
2628         if (typeof(b.listeners) == 'undefined') {
2629             b.listeners = { click : cb.createDelegate(this)  };
2630         }
2631         
2632         var btn = Roo.factory(b);
2633            
2634         btn.onRender(this.el.select('.modal-footer div').first());
2635         
2636         return btn;   
2637        
2638     },
2639     
2640     setDefaultButton : function(btn)
2641     {
2642         //this.el.select('.modal-footer').()
2643     },
2644     resizeTo: function(w,h)
2645     {
2646         // skip..
2647     },
2648     setContentSize  : function(w, h)
2649     {
2650         
2651     },
2652     onButtonClick: function(btn,e)
2653     {
2654         //Roo.log([a,b,c]);
2655         this.fireEvent('btnclick', btn.name, e);
2656     },
2657      /**
2658      * Set the title of the Dialog
2659      * @param {String} str new Title
2660      */
2661     setTitle: function(str) {
2662         this.titleEl.dom.innerHTML = str;    
2663     },
2664     /**
2665      * Set the body of the Dialog
2666      * @param {String} str new Title
2667      */
2668     setBody: function(str) {
2669         this.bodyEl.dom.innerHTML = str;    
2670     },
2671     /**
2672      * Set the body of the Dialog using the template
2673      * @param {Obj} data - apply this data to the template and replace the body contents.
2674      */
2675     applyBody: function(obj)
2676     {
2677         if (!this.tmpl) {
2678             Roo.log("Error - using apply Body without a template");
2679             //code
2680         }
2681         this.tmpl.overwrite(this.bodyEl, obj);
2682     }
2683     
2684 });
2685
2686
2687 Roo.apply(Roo.bootstrap.Modal,  {
2688     /**
2689          * Button config that displays a single OK button
2690          * @type Object
2691          */
2692         OK :  [{
2693             name : 'ok',
2694             weight : 'primary',
2695             html : 'OK'
2696         }], 
2697         /**
2698          * Button config that displays Yes and No buttons
2699          * @type Object
2700          */
2701         YESNO : [
2702             {
2703                 name  : 'no',
2704                 html : 'No'
2705             },
2706             {
2707                 name  :'yes',
2708                 weight : 'primary',
2709                 html : 'Yes'
2710             }
2711         ],
2712         
2713         /**
2714          * Button config that displays OK and Cancel buttons
2715          * @type Object
2716          */
2717         OKCANCEL : [
2718             {
2719                name : 'cancel',
2720                 html : 'Cancel'
2721             },
2722             {
2723                 name : 'ok',
2724                 weight : 'primary',
2725                 html : 'OK'
2726             }
2727         ],
2728         /**
2729          * Button config that displays Yes, No and Cancel buttons
2730          * @type Object
2731          */
2732         YESNOCANCEL : [
2733             {
2734                 name : 'yes',
2735                 weight : 'primary',
2736                 html : 'Yes'
2737             },
2738             {
2739                 name : 'no',
2740                 html : 'No'
2741             },
2742             {
2743                 name : 'cancel',
2744                 html : 'Cancel'
2745             }
2746         ]
2747 });
2748  
2749  /*
2750  * - LGPL
2751  *
2752  * messagebox - can be used as a replace
2753  * 
2754  */
2755 /**
2756  * @class Roo.MessageBox
2757  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2758  * Example usage:
2759  *<pre><code>
2760 // Basic alert:
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2762
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2765     if (btn == 'ok'){
2766         // process text value...
2767     }
2768 });
2769
2770 // Show a dialog using config options:
2771 Roo.Msg.show({
2772    title:'Save Changes?',
2773    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774    buttons: Roo.Msg.YESNOCANCEL,
2775    fn: processResult,
2776    animEl: 'elId'
2777 });
2778 </code></pre>
2779  * @singleton
2780  */
2781 Roo.bootstrap.MessageBox = function(){
2782     var dlg, opt, mask, waitTimer;
2783     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784     var buttons, activeTextEl, bwidth;
2785
2786     
2787     // private
2788     var handleButton = function(button){
2789         dlg.hide();
2790         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2791     };
2792
2793     // private
2794     var handleHide = function(){
2795         if(opt && opt.cls){
2796             dlg.el.removeClass(opt.cls);
2797         }
2798         //if(waitTimer){
2799         //    Roo.TaskMgr.stop(waitTimer);
2800         //    waitTimer = null;
2801         //}
2802     };
2803
2804     // private
2805     var updateButtons = function(b){
2806         var width = 0;
2807         if(!b){
2808             buttons["ok"].hide();
2809             buttons["cancel"].hide();
2810             buttons["yes"].hide();
2811             buttons["no"].hide();
2812             //dlg.footer.dom.style.display = 'none';
2813             return width;
2814         }
2815         dlg.footerEl.dom.style.display = '';
2816         for(var k in buttons){
2817             if(typeof buttons[k] != "function"){
2818                 if(b[k]){
2819                     buttons[k].show();
2820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821                     width += buttons[k].el.getWidth()+15;
2822                 }else{
2823                     buttons[k].hide();
2824                 }
2825             }
2826         }
2827         return width;
2828     };
2829
2830     // private
2831     var handleEsc = function(d, k, e){
2832         if(opt && opt.closable !== false){
2833             dlg.hide();
2834         }
2835         if(e){
2836             e.stopEvent();
2837         }
2838     };
2839
2840     return {
2841         /**
2842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843          * @return {Roo.BasicDialog} The BasicDialog element
2844          */
2845         getDialog : function(){
2846            if(!dlg){
2847                 dlg = new Roo.bootstrap.Modal( {
2848                     //draggable: true,
2849                     //resizable:false,
2850                     //constraintoviewport:false,
2851                     //fixedcenter:true,
2852                     //collapsible : false,
2853                     //shim:true,
2854                     //modal: true,
2855                   //  width:400,
2856                   //  height:100,
2857                     //buttonAlign:"center",
2858                     closeClick : function(){
2859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2860                             handleButton("no");
2861                         }else{
2862                             handleButton("cancel");
2863                         }
2864                     }
2865                 });
2866                 dlg.render();
2867                 dlg.on("hide", handleHide);
2868                 mask = dlg.mask;
2869                 //dlg.addKeyListener(27, handleEsc);
2870                 buttons = {};
2871                 this.buttons = buttons;
2872                 var bt = this.buttonText;
2873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2877                 Roo.log(buttons)
2878                 bodyEl = dlg.bodyEl.createChild({
2879
2880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881                         '<textarea class="roo-mb-textarea"></textarea>' +
2882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2883                 });
2884                 msgEl = bodyEl.dom.firstChild;
2885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886                 textboxEl.enableDisplayMode();
2887                 textboxEl.addKeyListener([10,13], function(){
2888                     if(dlg.isVisible() && opt && opt.buttons){
2889                         if(opt.buttons.ok){
2890                             handleButton("ok");
2891                         }else if(opt.buttons.yes){
2892                             handleButton("yes");
2893                         }
2894                     }
2895                 });
2896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897                 textareaEl.enableDisplayMode();
2898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899                 progressEl.enableDisplayMode();
2900                 var pf = progressEl.dom.firstChild;
2901                 if (pf) {
2902                     pp = Roo.get(pf.firstChild);
2903                     pp.setHeight(pf.offsetHeight);
2904                 }
2905                 
2906             }
2907             return dlg;
2908         },
2909
2910         /**
2911          * Updates the message box body text
2912          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913          * the XHTML-compliant non-breaking space character '&amp;#160;')
2914          * @return {Roo.MessageBox} This message box
2915          */
2916         updateText : function(text){
2917             if(!dlg.isVisible() && !opt.width){
2918                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2919             }
2920             msgEl.innerHTML = text || '&#160;';
2921       
2922             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2924             var w = Math.max(
2925                     Math.min(opt.width || cw , this.maxWidth), 
2926                     Math.max(opt.minWidth || this.minWidth, bwidth)
2927             );
2928             if(opt.prompt){
2929                 activeTextEl.setWidth(w);
2930             }
2931             if(dlg.isVisible()){
2932                 dlg.fixedcenter = false;
2933             }
2934             // to big, make it scroll. = But as usual stupid IE does not support
2935             // !important..
2936             
2937             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2940             } else {
2941                 bodyEl.dom.style.height = '';
2942                 bodyEl.dom.style.overflowY = '';
2943             }
2944             if (cw > w) {
2945                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2946             } else {
2947                 bodyEl.dom.style.overflowX = '';
2948             }
2949             
2950             dlg.setContentSize(w, bodyEl.getHeight());
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = true;
2953             }
2954             return this;
2955         },
2956
2957         /**
2958          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2959          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         updateProgress : function(value, text){
2965             if(text){
2966                 this.updateText(text);
2967             }
2968             if (pp) { // weird bug on my firefox - for some reason this is not defined
2969                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2970             }
2971             return this;
2972         },        
2973
2974         /**
2975          * Returns true if the message box is currently displayed
2976          * @return {Boolean} True if the message box is visible, else false
2977          */
2978         isVisible : function(){
2979             return dlg && dlg.isVisible();  
2980         },
2981
2982         /**
2983          * Hides the message box if it is displayed
2984          */
2985         hide : function(){
2986             if(this.isVisible()){
2987                 dlg.hide();
2988             }  
2989         },
2990
2991         /**
2992          * Displays a new message box, or reinitializes an existing message box, based on the config options
2993          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994          * The following config object properties are supported:
2995          * <pre>
2996 Property    Type             Description
2997 ----------  ---------------  ------------------------------------------------------------------------------------
2998 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2999                                    closes (defaults to undefined)
3000 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3003                                    progress and wait dialogs will ignore this property and always hide the
3004                                    close button as they can only be closed programmatically.
3005 cls               String           A custom CSS class to apply to the message box element
3006 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3007                                    displayed (defaults to 75)
3008 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3009                                    function will be btn (the name of the button that was clicked, if applicable,
3010                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3011                                    Progress and wait dialogs will ignore this option since they do not respond to
3012                                    user actions and can only be closed programmatically, so any required function
3013                                    should be called by the same code after it closes the dialog.
3014 icon              String           A CSS class that provides a background image to be used as an icon for
3015                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3017 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3018 modal             Boolean          False to allow user interaction with the page while the message box is
3019                                    displayed (defaults to true)
3020 msg               String           A string that will replace the existing message box body text (defaults
3021                                    to the XHTML-compliant non-breaking space character '&#160;')
3022 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3023 progress          Boolean          True to display a progress bar (defaults to false)
3024 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3027 title             String           The title text
3028 value             String           The string value to set into the active textbox element if displayed
3029 wait              Boolean          True to display a progress bar (defaults to false)
3030 width             Number           The width of the dialog in pixels
3031 </pre>
3032          *
3033          * Example usage:
3034          * <pre><code>
3035 Roo.Msg.show({
3036    title: 'Address',
3037    msg: 'Please enter your address:',
3038    width: 300,
3039    buttons: Roo.MessageBox.OKCANCEL,
3040    multiline: true,
3041    fn: saveAddress,
3042    animEl: 'addAddressBtn'
3043 });
3044 </code></pre>
3045          * @param {Object} config Configuration options
3046          * @return {Roo.MessageBox} This message box
3047          */
3048         show : function(options)
3049         {
3050             
3051             // this causes nightmares if you show one dialog after another
3052             // especially on callbacks..
3053              
3054             if(this.isVisible()){
3055                 
3056                 this.hide();
3057                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3059                 Roo.log("New Dialog Message:" +  options.msg )
3060                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3062                 
3063             }
3064             var d = this.getDialog();
3065             opt = options;
3066             d.setTitle(opt.title || "&#160;");
3067             d.closeEl.setDisplayed(opt.closable !== false);
3068             activeTextEl = textboxEl;
3069             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3070             if(opt.prompt){
3071                 if(opt.multiline){
3072                     textboxEl.hide();
3073                     textareaEl.show();
3074                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3075                         opt.multiline : this.defaultTextHeight);
3076                     activeTextEl = textareaEl;
3077                 }else{
3078                     textboxEl.show();
3079                     textareaEl.hide();
3080                 }
3081             }else{
3082                 textboxEl.hide();
3083                 textareaEl.hide();
3084             }
3085             progressEl.setDisplayed(opt.progress === true);
3086             this.updateProgress(0);
3087             activeTextEl.dom.value = opt.value || "";
3088             if(opt.prompt){
3089                 dlg.setDefaultButton(activeTextEl);
3090             }else{
3091                 var bs = opt.buttons;
3092                 var db = null;
3093                 if(bs && bs.ok){
3094                     db = buttons["ok"];
3095                 }else if(bs && bs.yes){
3096                     db = buttons["yes"];
3097                 }
3098                 dlg.setDefaultButton(db);
3099             }
3100             bwidth = updateButtons(opt.buttons);
3101             this.updateText(opt.msg);
3102             if(opt.cls){
3103                 d.el.addClass(opt.cls);
3104             }
3105             d.proxyDrag = opt.proxyDrag === true;
3106             d.modal = opt.modal !== false;
3107             d.mask = opt.modal !== false ? mask : false;
3108             if(!d.isVisible()){
3109                 // force it to the end of the z-index stack so it gets a cursor in FF
3110                 document.body.appendChild(dlg.el.dom);
3111                 d.animateTarget = null;
3112                 d.show(options.animEl);
3113             }
3114             return this;
3115         },
3116
3117         /**
3118          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3119          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120          * and closing the message box when the process is complete.
3121          * @param {String} title The title bar text
3122          * @param {String} msg The message box body text
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         progress : function(title, msg){
3126             this.show({
3127                 title : title,
3128                 msg : msg,
3129                 buttons: false,
3130                 progress:true,
3131                 closable:false,
3132                 minWidth: this.minProgressWidth,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140          * If a callback function is passed it will be called after the user clicks the button, and the
3141          * id of the button that was clicked will be passed as the only parameter to the callback
3142          * (could also be the top-right close button).
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146          * @param {Object} scope (optional) The scope of the callback function
3147          * @return {Roo.MessageBox} This message box
3148          */
3149         alert : function(title, msg, fn, scope){
3150             this.show({
3151                 title : title,
3152                 msg : msg,
3153                 buttons: this.OK,
3154                 fn: fn,
3155                 scope : scope,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3163          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164          * You are responsible for closing the message box when the process is complete.
3165          * @param {String} msg The message box body text
3166          * @param {String} title (optional) The title bar text
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         wait : function(msg, title){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: false,
3174                 closable:false,
3175                 progress:true,
3176                 modal:true,
3177                 width:300,
3178                 wait:true
3179             });
3180             waitTimer = Roo.TaskMgr.start({
3181                 run: function(i){
3182                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3183                 },
3184                 interval: 1000
3185             });
3186             return this;
3187         },
3188
3189         /**
3190          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193          * @param {String} title The title bar text
3194          * @param {String} msg The message box body text
3195          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196          * @param {Object} scope (optional) The scope of the callback function
3197          * @return {Roo.MessageBox} This message box
3198          */
3199         confirm : function(title, msg, fn, scope){
3200             this.show({
3201                 title : title,
3202                 msg : msg,
3203                 buttons: this.YESNO,
3204                 fn: fn,
3205                 scope : scope,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3214          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215          * (could also be the top-right close button) and the text that was entered will be passed as the two
3216          * parameters to the callback.
3217          * @param {String} title The title bar text
3218          * @param {String} msg The message box body text
3219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220          * @param {Object} scope (optional) The scope of the callback function
3221          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         prompt : function(title, msg, fn, scope, multiline){
3226             this.show({
3227                 title : title,
3228                 msg : msg,
3229                 buttons: this.OKCANCEL,
3230                 fn: fn,
3231                 minWidth:250,
3232                 scope : scope,
3233                 prompt:true,
3234                 multiline: multiline,
3235                 modal : true
3236             });
3237             return this;
3238         },
3239
3240         /**
3241          * Button config that displays a single OK button
3242          * @type Object
3243          */
3244         OK : {ok:true},
3245         /**
3246          * Button config that displays Yes and No buttons
3247          * @type Object
3248          */
3249         YESNO : {yes:true, no:true},
3250         /**
3251          * Button config that displays OK and Cancel buttons
3252          * @type Object
3253          */
3254         OKCANCEL : {ok:true, cancel:true},
3255         /**
3256          * Button config that displays Yes, No and Cancel buttons
3257          * @type Object
3258          */
3259         YESNOCANCEL : {yes:true, no:true, cancel:true},
3260
3261         /**
3262          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3263          * @type Number
3264          */
3265         defaultTextHeight : 75,
3266         /**
3267          * The maximum width in pixels of the message box (defaults to 600)
3268          * @type Number
3269          */
3270         maxWidth : 600,
3271         /**
3272          * The minimum width in pixels of the message box (defaults to 100)
3273          * @type Number
3274          */
3275         minWidth : 100,
3276         /**
3277          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3278          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3279          * @type Number
3280          */
3281         minProgressWidth : 250,
3282         /**
3283          * An object containing the default button text strings that can be overriden for localized language support.
3284          * Supported properties are: ok, cancel, yes and no.
3285          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3286          * @type Object
3287          */
3288         buttonText : {
3289             ok : "OK",
3290             cancel : "Cancel",
3291             yes : "Yes",
3292             no : "No"
3293         }
3294     };
3295 }();
3296
3297 /**
3298  * Shorthand for {@link Roo.MessageBox}
3299  */
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3302 /*
3303  * - LGPL
3304  *
3305  * navbar
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.Navbar
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap Navbar class
3313
3314  * @constructor
3315  * Create a new Navbar
3316  * @param {Object} config The config object
3317  */
3318
3319
3320 Roo.bootstrap.Navbar = function(config){
3321     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3326     
3327     
3328    
3329     // private
3330     navItems : false,
3331     loadMask : false,
3332     
3333     
3334     getAutoCreate : function(){
3335         
3336         
3337         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3338         
3339     },
3340     
3341     initEvents :function ()
3342     {
3343         //Roo.log(this.el.select('.navbar-toggle',true));
3344         this.el.select('.navbar-toggle',true).on('click', function() {
3345            // Roo.log('click');
3346             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3347         }, this);
3348         
3349         var mark = {
3350             tag: "div",
3351             cls:"x-dlg-mask"
3352         };
3353         
3354         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3355         
3356         var size = this.el.getSize();
3357         this.maskEl.setSize(size.width, size.height);
3358         this.maskEl.enableDisplayMode("block");
3359         this.maskEl.hide();
3360         
3361         if(this.loadMask){
3362             this.maskEl.show();
3363         }
3364     },
3365     
3366     
3367     getChildContainer : function()
3368     {
3369         if (this.el.select('.collapse').getCount()) {
3370             return this.el.select('.collapse',true).first();
3371         }
3372         
3373         return this.el;
3374     },
3375     
3376     mask : function()
3377     {
3378         this.maskEl.show();
3379     },
3380     
3381     unmask : function()
3382     {
3383         this.maskEl.hide();
3384     } 
3385     
3386     
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSimplebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  *
3407  * @cfg {Boolean} inverse is inverted color
3408  * 
3409  * @cfg {String} type (nav | pills | tabs)
3410  * @cfg {Boolean} arrangement stacked | justified
3411  * @cfg {String} align (left | right) alignment
3412  * 
3413  * @cfg {Boolean} main (true|false) main nav bar? default false
3414  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3415  * 
3416  * @cfg {String} tag (header|footer|nav|div) default is nav 
3417
3418  * 
3419  * 
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSimplebar = function(config){
3428     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3432     
3433     inverse: false,
3434     
3435     type: false,
3436     arrangement: '',
3437     align : false,
3438     
3439     
3440     
3441     main : false,
3442     
3443     
3444     tag : false,
3445     
3446     
3447     getAutoCreate : function(){
3448         
3449         
3450         var cfg = {
3451             tag : this.tag || 'div',
3452             cls : 'navbar'
3453         };
3454           
3455         
3456         cfg.cn = [
3457             {
3458                 cls: 'nav',
3459                 tag : 'ul'
3460             }
3461         ];
3462         
3463          
3464         this.type = this.type || 'nav';
3465         if (['tabs','pills'].indexOf(this.type)!==-1) {
3466             cfg.cn[0].cls += ' nav-' + this.type
3467         
3468         
3469         } else {
3470             if (this.type!=='nav') {
3471                 Roo.log('nav type must be nav/tabs/pills')
3472             }
3473             cfg.cn[0].cls += ' navbar-nav'
3474         }
3475         
3476         
3477         
3478         
3479         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480             cfg.cn[0].cls += ' nav-' + this.arrangement;
3481         }
3482         
3483         
3484         if (this.align === 'right') {
3485             cfg.cn[0].cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     
3496         
3497     }
3498     
3499     
3500     
3501 });
3502
3503
3504
3505  
3506
3507  
3508        /*
3509  * - LGPL
3510  *
3511  * navbar
3512  * 
3513  */
3514
3515 /**
3516  * @class Roo.bootstrap.NavHeaderbar
3517  * @extends Roo.bootstrap.NavSimplebar
3518  * Bootstrap Sidebar class
3519  *
3520  * @cfg {String} brand what is brand
3521  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522  * @cfg {String} brand_href href of the brand
3523  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3524  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3527  * 
3528  * @constructor
3529  * Create a new Sidebar
3530  * @param {Object} config The config object
3531  */
3532
3533
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3536       
3537 };
3538
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3540     
3541     position: '',
3542     brand: '',
3543     brand_href: false,
3544     srButton : true,
3545     autohide : false,
3546     desktopCenter : false,
3547    
3548     
3549     getAutoCreate : function(){
3550         
3551         var   cfg = {
3552             tag: this.nav || 'nav',
3553             cls: 'navbar',
3554             role: 'navigation',
3555             cn: []
3556         };
3557         
3558         var cn = cfg.cn;
3559         if (this.desktopCenter) {
3560             cn.push({cls : 'container', cn : []});
3561             cn = cn[0].cn;
3562         }
3563         
3564         if(this.srButton){
3565             cn.push({
3566                 tag: 'div',
3567                 cls: 'navbar-header',
3568                 cn: [
3569                     {
3570                         tag: 'button',
3571                         type: 'button',
3572                         cls: 'navbar-toggle',
3573                         'data-toggle': 'collapse',
3574                         cn: [
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'sr-only',
3578                                 html: 'Toggle navigation'
3579                             },
3580                             {
3581                                 tag: 'span',
3582                                 cls: 'icon-bar'
3583                             },
3584                             {
3585                                 tag: 'span',
3586                                 cls: 'icon-bar'
3587                             },
3588                             {
3589                                 tag: 'span',
3590                                 cls: 'icon-bar'
3591                             }
3592                         ]
3593                     }
3594                 ]
3595             });
3596         }
3597         
3598         cn.push({
3599             tag: 'div',
3600             cls: 'collapse navbar-collapse',
3601             cn : []
3602         });
3603         
3604         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3605         
3606         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607             cfg.cls += ' navbar-' + this.position;
3608             
3609             // tag can override this..
3610             
3611             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3612         }
3613         
3614         if (this.brand !== '') {
3615             cn[0].cn.push({
3616                 tag: 'a',
3617                 href: this.brand_href ? this.brand_href : '#',
3618                 cls: 'navbar-brand',
3619                 cn: [
3620                 this.brand
3621                 ]
3622             });
3623         }
3624         
3625         if(this.main){
3626             cfg.cls += ' main-nav';
3627         }
3628         
3629         
3630         return cfg;
3631
3632         
3633     },
3634     getHeaderChildContainer : function()
3635     {
3636         if (this.el.select('.navbar-header').getCount()) {
3637             return this.el.select('.navbar-header',true).first();
3638         }
3639         
3640         return this.getChildContainer();
3641     },
3642     
3643     
3644     initEvents : function()
3645     {
3646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3647         
3648         if (this.autohide) {
3649             
3650             var prevScroll = 0;
3651             var ft = this.el;
3652             
3653             Roo.get(document).on('scroll',function(e) {
3654                 var ns = Roo.get(document).getScroll().top;
3655                 var os = prevScroll;
3656                 prevScroll = ns;
3657                 
3658                 if(ns > os){
3659                     ft.removeClass('slideDown');
3660                     ft.addClass('slideUp');
3661                     return;
3662                 }
3663                 ft.removeClass('slideUp');
3664                 ft.addClass('slideDown');
3665                  
3666               
3667           },this);
3668         }
3669     }    
3670     
3671 });
3672
3673
3674
3675  
3676
3677  /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavSidebar
3686  * @extends Roo.bootstrap.Navbar
3687  * Bootstrap Sidebar class
3688  * 
3689  * @constructor
3690  * Create a new Sidebar
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.NavSidebar = function(config){
3696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3697 };
3698
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3700     
3701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3702     
3703     getAutoCreate : function(){
3704         
3705         
3706         return  {
3707             tag: 'div',
3708             cls: 'sidebar sidebar-nav'
3709         };
3710     
3711         
3712     }
3713     
3714     
3715     
3716 });
3717
3718
3719
3720  
3721
3722  /*
3723  * - LGPL
3724  *
3725  * nav group
3726  * 
3727  */
3728
3729 /**
3730  * @class Roo.bootstrap.NavGroup
3731  * @extends Roo.bootstrap.Component
3732  * Bootstrap NavGroup class
3733  * @cfg {String} align (left|right)
3734  * @cfg {Boolean} inverse
3735  * @cfg {String} type (nav|pills|tab) default nav
3736  * @cfg {String} navId - reference Id for navbar.
3737
3738  * 
3739  * @constructor
3740  * Create a new nav group
3741  * @param {Object} config The config object
3742  */
3743
3744 Roo.bootstrap.NavGroup = function(config){
3745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3746     this.navItems = [];
3747    
3748     Roo.bootstrap.NavGroup.register(this);
3749      this.addEvents({
3750         /**
3751              * @event changed
3752              * Fires when the active item changes
3753              * @param {Roo.bootstrap.NavGroup} this
3754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3756          */
3757         'changed': true
3758      });
3759     
3760 };
3761
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3763     
3764     align: '',
3765     inverse: false,
3766     form: false,
3767     type: 'nav',
3768     navId : '',
3769     // private
3770     
3771     navItems : false, 
3772     
3773     getAutoCreate : function()
3774     {
3775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3776         
3777         cfg = {
3778             tag : 'ul',
3779             cls: 'nav' 
3780         };
3781         
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cls += ' nav-' + this.type
3784         } else {
3785             if (this.type!=='nav') {
3786                 Roo.log('nav type must be nav/tabs/pills')
3787             }
3788             cfg.cls += ' navbar-nav'
3789         }
3790         
3791         if (this.parent().sidebar) {
3792             cfg = {
3793                 tag: 'ul',
3794                 cls: 'dashboard-menu sidebar-menu'
3795             };
3796             
3797             return cfg;
3798         }
3799         
3800         if (this.form === true) {
3801             cfg = {
3802                 tag: 'form',
3803                 cls: 'navbar-form'
3804             };
3805             
3806             if (this.align === 'right') {
3807                 cfg.cls += ' navbar-right';
3808             } else {
3809                 cfg.cls += ' navbar-left';
3810             }
3811         }
3812         
3813         if (this.align === 'right') {
3814             cfg.cls += ' navbar-right';
3815         }
3816         
3817         if (this.inverse) {
3818             cfg.cls += ' navbar-inverse';
3819             
3820         }
3821         
3822         
3823         return cfg;
3824     },
3825     /**
3826     * sets the active Navigation item
3827     * @param {Roo.bootstrap.NavItem} the new current navitem
3828     */
3829     setActiveItem : function(item)
3830     {
3831         var prev = false;
3832         Roo.each(this.navItems, function(v){
3833             if (v == item) {
3834                 return ;
3835             }
3836             if (v.isActive()) {
3837                 v.setActive(false, true);
3838                 prev = v;
3839                 
3840             }
3841             
3842         });
3843
3844         item.setActive(true, true);
3845         this.fireEvent('changed', this, item, prev);
3846         
3847         
3848     },
3849     /**
3850     * gets the active Navigation item
3851     * @return {Roo.bootstrap.NavItem} the current navitem
3852     */
3853     getActive : function()
3854     {
3855         
3856         var prev = false;
3857         Roo.each(this.navItems, function(v){
3858             
3859             if (v.isActive()) {
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865         return prev;
3866     },
3867     
3868     indexOfNav : function()
3869     {
3870         
3871         var prev = false;
3872         Roo.each(this.navItems, function(v,i){
3873             
3874             if (v.isActive()) {
3875                 prev = i;
3876                 
3877             }
3878             
3879         });
3880         return prev;
3881     },
3882     /**
3883     * adds a Navigation item
3884     * @param {Roo.bootstrap.NavItem} the navitem to add
3885     */
3886     addItem : function(cfg)
3887     {
3888         var cn = new Roo.bootstrap.NavItem(cfg);
3889         this.register(cn);
3890         cn.parentId = this.id;
3891         cn.onRender(this.el, null);
3892         return cn;
3893     },
3894     /**
3895     * register a Navigation item
3896     * @param {Roo.bootstrap.NavItem} the navitem to add
3897     */
3898     register : function(item)
3899     {
3900         this.navItems.push( item);
3901         item.navId = this.navId;
3902     
3903     },
3904     
3905     /**
3906     * clear all the Navigation item
3907     */
3908    
3909     clearAll : function()
3910     {
3911         this.navItems = [];
3912         this.el.dom.innerHTML = '';
3913     },
3914     
3915     getNavItem: function(tabId)
3916     {
3917         var ret = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.tabId == tabId) {
3920                ret =  e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return ret;
3927     },
3928     
3929     setActiveNext : function()
3930     {
3931         var i = this.indexOfNav(this.getActive());
3932         if (i > this.navItems.length) {
3933             return;
3934         }
3935         this.setActiveItem(this.navItems[i+1]);
3936     },
3937     setActivePrev : function()
3938     {
3939         var i = this.indexOfNav(this.getActive());
3940         if (i  < 1) {
3941             return;
3942         }
3943         this.setActiveItem(this.navItems[i-1]);
3944     },
3945     clearWasActive : function(except) {
3946         Roo.each(this.navItems, function(e) {
3947             if (e.tabId != except.tabId && e.was_active) {
3948                e.was_active = false;
3949                return false;
3950             }
3951             return true;
3952             
3953         });
3954     },
3955     getWasActive : function ()
3956     {
3957         var r = false;
3958         Roo.each(this.navItems, function(e) {
3959             if (e.was_active) {
3960                r = e;
3961                return false;
3962             }
3963             return true;
3964             
3965         });
3966         return r;
3967     }
3968     
3969     
3970 });
3971
3972  
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3974     
3975     groups: {},
3976      /**
3977     * register a Navigation Group
3978     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3979     */
3980     register : function(navgrp)
3981     {
3982         this.groups[navgrp.navId] = navgrp;
3983         
3984     },
3985     /**
3986     * fetch a Navigation Group based on the navigation ID
3987     * @param {string} the navgroup to add
3988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3989     */
3990     get: function(navId) {
3991         if (typeof(this.groups[navId]) == 'undefined') {
3992             return false;
3993             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3994         }
3995         return this.groups[navId] ;
3996     }
3997     
3998     
3999     
4000 });
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Navbar.NavItem class
4013  * @cfg {String} href  link to
4014  * @cfg {String} html content of button
4015  * @cfg {String} badge text inside badge
4016  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017  * @cfg {String} glyphicon name of glyphicon
4018  * @cfg {String} icon name of font awesome icon
4019  * @cfg {Boolean} active Is item active
4020  * @cfg {Boolean} disabled Is item disabled
4021  
4022  * @cfg {Boolean} preventDefault (true | false) default false
4023  * @cfg {String} tabId the tab that this item activates.
4024  * @cfg {String} tagtype (a|span) render as a href or span?
4025  * @cfg {Boolean} animateRef (true|false) link to element default false  
4026   
4027  * @constructor
4028  * Create a new Navbar Item
4029  * @param {Object} config The config object
4030  */
4031 Roo.bootstrap.NavItem = function(config){
4032     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4033     this.addEvents({
4034         // raw events
4035         /**
4036          * @event click
4037          * The raw click event for the entire grid.
4038          * @param {Roo.EventObject} e
4039          */
4040         "click" : true,
4041          /**
4042             * @event changed
4043             * Fires when the active item active state changes
4044             * @param {Roo.bootstrap.NavItem} this
4045             * @param {boolean} state the new state
4046              
4047          */
4048         'changed': true,
4049         /**
4050             * @event scrollto
4051             * Fires when scroll to element
4052             * @param {Roo.bootstrap.NavItem} this
4053             * @param {Object} options
4054             * @param {Roo.EventObject} e
4055              
4056          */
4057         'scrollto': true
4058     });
4059    
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4063     
4064     href: false,
4065     html: '',
4066     badge: '',
4067     icon: false,
4068     glyphicon: false,
4069     active: false,
4070     preventDefault : false,
4071     tabId : false,
4072     tagtype : 'a',
4073     disabled : false,
4074     animateRef : false,
4075     was_active : false,
4076     
4077     getAutoCreate : function(){
4078          
4079         var cfg = {
4080             tag: 'li',
4081             cls: 'nav-item'
4082             
4083         };
4084         
4085         if (this.active) {
4086             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087         }
4088         if (this.disabled) {
4089             cfg.cls += ' disabled';
4090         }
4091         
4092         if (this.href || this.html || this.glyphicon || this.icon) {
4093             cfg.cn = [
4094                 {
4095                     tag: this.tagtype,
4096                     href : this.href || "#",
4097                     html: this.html || ''
4098                 }
4099             ];
4100             
4101             if (this.icon) {
4102                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4103             }
4104
4105             if(this.glyphicon) {
4106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4107             }
4108             
4109             if (this.menu) {
4110                 
4111                 cfg.cn[0].html += " <span class='caret'></span>";
4112              
4113             }
4114             
4115             if (this.badge !== '') {
4116                  
4117                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4118             }
4119         }
4120         
4121         
4122         
4123         return cfg;
4124     },
4125     initEvents: function() 
4126     {
4127         if (typeof (this.menu) != 'undefined') {
4128             this.menu.parentType = this.xtype;
4129             this.menu.triggerEl = this.el;
4130             this.menu = this.addxtype(Roo.apply({}, this.menu));
4131         }
4132         
4133         this.el.select('a',true).on('click', this.onClick, this);
4134         
4135         if(this.tagtype == 'span'){
4136             this.el.select('span',true).on('click', this.onClick, this);
4137         }
4138        
4139         // at this point parent should be available..
4140         this.parent().register(this);
4141     },
4142     
4143     onClick : function(e)
4144     {
4145         if(
4146                 this.preventDefault || 
4147                 this.href == '#' 
4148         ){
4149             
4150             e.preventDefault();
4151         }
4152         
4153         if (this.disabled) {
4154             return;
4155         }
4156         
4157         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158         if (tg && tg.transition) {
4159             Roo.log("waiting for the transitionend");
4160             return;
4161         }
4162         
4163         
4164         
4165         //Roo.log("fire event clicked");
4166         if(this.fireEvent('click', this, e) === false){
4167             return;
4168         };
4169         
4170         if(this.tagtype == 'span'){
4171             return;
4172         }
4173         
4174         //Roo.log(this.href);
4175         var ael = this.el.select('a',true).first();
4176         //Roo.log(ael);
4177         
4178         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181                 return; // ignore... - it's a 'hash' to another page.
4182             }
4183             
4184             e.preventDefault();
4185             this.scrollToElement(e);
4186         }
4187         
4188         
4189         var p =  this.parent();
4190    
4191         if (['tabs','pills'].indexOf(p.type)!==-1) {
4192             if (typeof(p.setActiveItem) !== 'undefined') {
4193                 p.setActiveItem(this);
4194             }
4195         }
4196         
4197         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199             // remove the collapsed menu expand...
4200             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4201         }
4202     },
4203     
4204     isActive: function () {
4205         return this.active
4206     },
4207     setActive : function(state, fire, is_was_active)
4208     {
4209         if (this.active && !state && this.navId) {
4210             this.was_active = true;
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 nv.clearWasActive(this);
4214             }
4215             
4216         }
4217         this.active = state;
4218         
4219         if (!state ) {
4220             this.el.removeClass('active');
4221         } else if (!this.el.hasClass('active')) {
4222             this.el.addClass('active');
4223         }
4224         if (fire) {
4225             this.fireEvent('changed', this, state);
4226         }
4227         
4228         // show a panel if it's registered and related..
4229         
4230         if (!this.navId || !this.tabId || !state || is_was_active) {
4231             return;
4232         }
4233         
4234         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4235         if (!tg) {
4236             return;
4237         }
4238         var pan = tg.getPanelByName(this.tabId);
4239         if (!pan) {
4240             return;
4241         }
4242         // if we can not flip to new panel - go back to old nav highlight..
4243         if (false == tg.showPanel(pan)) {
4244             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245             if (nv) {
4246                 var onav = nv.getWasActive();
4247                 if (onav) {
4248                     onav.setActive(true, false, true);
4249                 }
4250             }
4251             
4252         }
4253         
4254         
4255         
4256     },
4257      // this should not be here...
4258     setDisabled : function(state)
4259     {
4260         this.disabled = state;
4261         if (!state ) {
4262             this.el.removeClass('disabled');
4263         } else if (!this.el.hasClass('disabled')) {
4264             this.el.addClass('disabled');
4265         }
4266         
4267     },
4268     
4269     /**
4270      * Fetch the element to display the tooltip on.
4271      * @return {Roo.Element} defaults to this.el
4272      */
4273     tooltipEl : function()
4274     {
4275         return this.el.select('' + this.tagtype + '', true).first();
4276     },
4277     
4278     scrollToElement : function(e)
4279     {
4280         var c = document.body;
4281         
4282         /*
4283          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284          */
4285         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286             c = document.documentElement;
4287         }
4288         
4289         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4290         
4291         if(!target){
4292             return;
4293         }
4294
4295         var o = target.calcOffsetsTo(c);
4296         
4297         var options = {
4298             target : target,
4299             value : o[1]
4300         };
4301         
4302         this.fireEvent('scrollto', this, options, e);
4303         
4304         Roo.get(c).scrollTo('top', options.value, true);
4305         
4306         return;
4307     }
4308 });
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * sidebar item
4315  *
4316  *  li
4317  *    <span> icon </span>
4318  *    <span> text </span>
4319  *    <span>badge </span>
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.NavSidebarItem
4324  * @extends Roo.bootstrap.NavItem
4325  * Bootstrap Navbar.NavSidebarItem class
4326  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327  * @constructor
4328  * Create a new Navbar Button
4329  * @param {Object} config The config object
4330  */
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event click
4337          * The raw click event for the entire grid.
4338          * @param {Roo.EventObject} e
4339          */
4340         "click" : true,
4341          /**
4342             * @event changed
4343             * Fires when the active item active state changes
4344             * @param {Roo.bootstrap.NavSidebarItem} this
4345             * @param {boolean} state the new state
4346              
4347          */
4348         'changed': true
4349     });
4350    
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4354     
4355     badgeWeight : 'default',
4356     
4357     getAutoCreate : function(){
4358         
4359         
4360         var a = {
4361                 tag: 'a',
4362                 href : this.href || '#',
4363                 cls: '',
4364                 html : '',
4365                 cn : []
4366         };
4367         var cfg = {
4368             tag: 'li',
4369             cls: '',
4370             cn: [ a ]
4371         };
4372         var span = {
4373             tag: 'span',
4374             html : this.html || ''
4375         };
4376         
4377         
4378         if (this.active) {
4379             cfg.cls += ' active';
4380         }
4381         
4382         if (this.disabled) {
4383             cfg.cls += ' disabled';
4384         }
4385         
4386         // left icon..
4387         if (this.glyphicon || this.icon) {
4388             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4389             a.cn.push({ tag : 'i', cls : c }) ;
4390         }
4391         // html..
4392         a.cn.push(span);
4393         // then badge..
4394         if (this.badge !== '') {
4395             
4396             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4397         }
4398         // fi
4399         if (this.menu) {
4400             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401             a.cls += 'dropdown-toggle treeview' ;
4402             
4403         }
4404         
4405         
4406         
4407         return cfg;
4408          
4409            
4410     },
4411     
4412     initEvents : function()
4413     { 
4414         this.el.on('click', this.onClick, this);
4415        
4416     
4417         if(this.badge !== ''){
4418  
4419             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4420         }
4421         
4422     },
4423     
4424     onClick : function(e)
4425     {
4426         if(this.disabled){
4427             e.preventDefault();
4428             return;
4429         }
4430         
4431         if(this.preventDefault){
4432             e.preventDefault();
4433         }
4434         
4435         this.fireEvent('click', this);
4436     },
4437     
4438     disable : function()
4439     {
4440         this.setDisabled(true);
4441     },
4442     
4443     enable : function()
4444     {
4445         this.setDisabled(false);
4446     },
4447     
4448     setDisabled : function(state)
4449     {
4450         if(this.disabled == state){
4451             return;
4452         }
4453         
4454         this.disabled = state;
4455         
4456         if (state) {
4457             this.el.addClass('disabled');
4458             return;
4459         }
4460         
4461         this.el.removeClass('disabled');
4462         
4463         return;
4464     },
4465     
4466     setActive : function(state)
4467     {
4468         if(this.active == state){
4469             return;
4470         }
4471         
4472         this.active = state;
4473         
4474         if (state) {
4475             this.el.addClass('active');
4476             return;
4477         }
4478         
4479         this.el.removeClass('active');
4480         
4481         return;
4482     },
4483     
4484     isActive: function () 
4485     {
4486         return this.active;
4487     },
4488     
4489     setBadge : function(str)
4490     {
4491         if(!this.badgeEl){
4492             return;
4493         }
4494         
4495         this.badgeEl.dom.innerHTML = str;
4496     }
4497     
4498    
4499      
4500  
4501 });
4502  
4503
4504  /*
4505  * - LGPL
4506  *
4507  * row
4508  * 
4509  */
4510
4511 /**
4512  * @class Roo.bootstrap.Row
4513  * @extends Roo.bootstrap.Component
4514  * Bootstrap Row class (contains columns...)
4515  * 
4516  * @constructor
4517  * Create a new Row
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Row = function(config){
4522     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4526     
4527     getAutoCreate : function(){
4528        return {
4529             cls: 'row clearfix'
4530        };
4531     }
4532     
4533     
4534 });
4535
4536  
4537
4538  /*
4539  * - LGPL
4540  *
4541  * element
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Element
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Element class
4549  * @cfg {String} html contents of the element
4550  * @cfg {String} tag tag of the element
4551  * @cfg {String} cls class of the element
4552  * @cfg {Boolean} preventDefault (true|false) default false
4553  * @cfg {Boolean} clickable (true|false) default false
4554  * 
4555  * @constructor
4556  * Create a new Element
4557  * @param {Object} config The config object
4558  */
4559
4560 Roo.bootstrap.Element = function(config){
4561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4562     
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * When a element is chick
4568          * @param {Roo.bootstrap.Element} this
4569          * @param {Roo.EventObject} e
4570          */
4571         "click" : true
4572     });
4573 };
4574
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4576     
4577     tag: 'div',
4578     cls: '',
4579     html: '',
4580     preventDefault: false, 
4581     clickable: false,
4582     
4583     getAutoCreate : function(){
4584         
4585         var cfg = {
4586             tag: this.tag,
4587             cls: this.cls,
4588             html: this.html
4589         };
4590         
4591         return cfg;
4592     },
4593     
4594     initEvents: function() 
4595     {
4596         Roo.bootstrap.Element.superclass.initEvents.call(this);
4597         
4598         if(this.clickable){
4599             this.el.on('click', this.onClick, this);
4600         }
4601         
4602     },
4603     
4604     onClick : function(e)
4605     {
4606         if(this.preventDefault){
4607             e.preventDefault();
4608         }
4609         
4610         this.fireEvent('click', this, e);
4611     },
4612     
4613     getValue : function()
4614     {
4615         return this.el.dom.innerHTML;
4616     },
4617     
4618     setValue : function(value)
4619     {
4620         this.el.dom.innerHTML = value;
4621     }
4622    
4623 });
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * pagination
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.Pagination
4636  * @extends Roo.bootstrap.Component
4637  * Bootstrap Pagination class
4638  * @cfg {String} size xs | sm | md | lg
4639  * @cfg {Boolean} inverse false | true
4640  * 
4641  * @constructor
4642  * Create a new Pagination
4643  * @param {Object} config The config object
4644  */
4645
4646 Roo.bootstrap.Pagination = function(config){
4647     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4648 };
4649
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4651     
4652     cls: false,
4653     size: false,
4654     inverse: false,
4655     
4656     getAutoCreate : function(){
4657         var cfg = {
4658             tag: 'ul',
4659                 cls: 'pagination'
4660         };
4661         if (this.inverse) {
4662             cfg.cls += ' inverse';
4663         }
4664         if (this.html) {
4665             cfg.html=this.html;
4666         }
4667         if (this.cls) {
4668             cfg.cls += " " + this.cls;
4669         }
4670         return cfg;
4671     }
4672    
4673 });
4674
4675  
4676
4677  /*
4678  * - LGPL
4679  *
4680  * Pagination item
4681  * 
4682  */
4683
4684
4685 /**
4686  * @class Roo.bootstrap.PaginationItem
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap PaginationItem class
4689  * @cfg {String} html text
4690  * @cfg {String} href the link
4691  * @cfg {Boolean} preventDefault (true | false) default true
4692  * @cfg {Boolean} active (true | false) default false
4693  * @cfg {Boolean} disabled default false
4694  * 
4695  * 
4696  * @constructor
4697  * Create a new PaginationItem
4698  * @param {Object} config The config object
4699  */
4700
4701
4702 Roo.bootstrap.PaginationItem = function(config){
4703     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4704     this.addEvents({
4705         // raw events
4706         /**
4707          * @event click
4708          * The raw click event for the entire grid.
4709          * @param {Roo.EventObject} e
4710          */
4711         "click" : true
4712     });
4713 };
4714
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4716     
4717     href : false,
4718     html : false,
4719     preventDefault: true,
4720     active : false,
4721     cls : false,
4722     disabled: false,
4723     
4724     getAutoCreate : function(){
4725         var cfg= {
4726             tag: 'li',
4727             cn: [
4728                 {
4729                     tag : 'a',
4730                     href : this.href ? this.href : '#',
4731                     html : this.html ? this.html : ''
4732                 }
4733             ]
4734         };
4735         
4736         if(this.cls){
4737             cfg.cls = this.cls;
4738         }
4739         
4740         if(this.disabled){
4741             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4742         }
4743         
4744         if(this.active){
4745             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4746         }
4747         
4748         return cfg;
4749     },
4750     
4751     initEvents: function() {
4752         
4753         this.el.on('click', this.onClick, this);
4754         
4755     },
4756     onClick : function(e)
4757     {
4758         Roo.log('PaginationItem on click ');
4759         if(this.preventDefault){
4760             e.preventDefault();
4761         }
4762         
4763         if(this.disabled){
4764             return;
4765         }
4766         
4767         this.fireEvent('click', this, e);
4768     }
4769    
4770 });
4771
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * slider
4778  * 
4779  */
4780
4781
4782 /**
4783  * @class Roo.bootstrap.Slider
4784  * @extends Roo.bootstrap.Component
4785  * Bootstrap Slider class
4786  *    
4787  * @constructor
4788  * Create a new Slider
4789  * @param {Object} config The config object
4790  */
4791
4792 Roo.bootstrap.Slider = function(config){
4793     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4794 };
4795
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4797     
4798     getAutoCreate : function(){
4799         
4800         var cfg = {
4801             tag: 'div',
4802             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4803             cn: [
4804                 {
4805                     tag: 'a',
4806                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4807                 }
4808             ]
4809         };
4810         
4811         return cfg;
4812     }
4813    
4814 });
4815
4816  /*
4817  * Based on:
4818  * Ext JS Library 1.1.1
4819  * Copyright(c) 2006-2007, Ext JS, LLC.
4820  *
4821  * Originally Released Under LGPL - original licence link has changed is not relivant.
4822  *
4823  * Fork - LGPL
4824  * <script type="text/javascript">
4825  */
4826  
4827
4828 /**
4829  * @class Roo.grid.ColumnModel
4830  * @extends Roo.util.Observable
4831  * This is the default implementation of a ColumnModel used by the Grid. It defines
4832  * the columns in the grid.
4833  * <br>Usage:<br>
4834  <pre><code>
4835  var colModel = new Roo.grid.ColumnModel([
4836         {header: "Ticker", width: 60, sortable: true, locked: true},
4837         {header: "Company Name", width: 150, sortable: true},
4838         {header: "Market Cap.", width: 100, sortable: true},
4839         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840         {header: "Employees", width: 100, sortable: true, resizable: false}
4841  ]);
4842  </code></pre>
4843  * <p>
4844  
4845  * The config options listed for this class are options which may appear in each
4846  * individual column definition.
4847  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848  * @constructor
4849  * @param {Object} config An Array of column config objects. See this class's
4850  * config objects for details.
4851 */
4852 Roo.grid.ColumnModel = function(config){
4853         /**
4854      * The config passed into the constructor
4855      */
4856     this.config = config;
4857     this.lookup = {};
4858
4859     // if no id, create one
4860     // if the column does not have a dataIndex mapping,
4861     // map it to the order it is in the config
4862     for(var i = 0, len = config.length; i < len; i++){
4863         var c = config[i];
4864         if(typeof c.dataIndex == "undefined"){
4865             c.dataIndex = i;
4866         }
4867         if(typeof c.renderer == "string"){
4868             c.renderer = Roo.util.Format[c.renderer];
4869         }
4870         if(typeof c.id == "undefined"){
4871             c.id = Roo.id();
4872         }
4873         if(c.editor && c.editor.xtype){
4874             c.editor  = Roo.factory(c.editor, Roo.grid);
4875         }
4876         if(c.editor && c.editor.isFormField){
4877             c.editor = new Roo.grid.GridEditor(c.editor);
4878         }
4879         this.lookup[c.id] = c;
4880     }
4881
4882     /**
4883      * The width of columns which have no width specified (defaults to 100)
4884      * @type Number
4885      */
4886     this.defaultWidth = 100;
4887
4888     /**
4889      * Default sortable of columns which have no sortable specified (defaults to false)
4890      * @type Boolean
4891      */
4892     this.defaultSortable = false;
4893
4894     this.addEvents({
4895         /**
4896              * @event widthchange
4897              * Fires when the width of a column changes.
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Number} newWidth The new width
4901              */
4902             "widthchange": true,
4903         /**
4904              * @event headerchange
4905              * Fires when the text of a header changes.
4906              * @param {ColumnModel} this
4907              * @param {Number} columnIndex The column index
4908              * @param {Number} newText The new header text
4909              */
4910             "headerchange": true,
4911         /**
4912              * @event hiddenchange
4913              * Fires when a column is hidden or "unhidden".
4914              * @param {ColumnModel} this
4915              * @param {Number} columnIndex The column index
4916              * @param {Boolean} hidden true if hidden, false otherwise
4917              */
4918             "hiddenchange": true,
4919             /**
4920          * @event columnmoved
4921          * Fires when a column is moved.
4922          * @param {ColumnModel} this
4923          * @param {Number} oldIndex
4924          * @param {Number} newIndex
4925          */
4926         "columnmoved" : true,
4927         /**
4928          * @event columlockchange
4929          * Fires when a column's locked state is changed
4930          * @param {ColumnModel} this
4931          * @param {Number} colIndex
4932          * @param {Boolean} locked true if locked
4933          */
4934         "columnlockchange" : true
4935     });
4936     Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 };
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939     /**
4940      * @cfg {String} header The header text to display in the Grid view.
4941      */
4942     /**
4943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945      * specified, the column's index is used as an index into the Record's data Array.
4946      */
4947     /**
4948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4950      */
4951     /**
4952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953      * Defaults to the value of the {@link #defaultSortable} property.
4954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4955      */
4956     /**
4957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4958      */
4959     /**
4960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4961      */
4962     /**
4963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4964      */
4965     /**
4966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4967      */
4968     /**
4969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4973      */
4974        /**
4975      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4976      */
4977     /**
4978      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4979      */
4980     /**
4981      * @cfg {String} cursor (Optional)
4982      */
4983     /**
4984      * @cfg {String} tooltip (Optional)
4985      */
4986     /**
4987      * @cfg {Number} xs (Optional)
4988      */
4989     /**
4990      * @cfg {Number} sm (Optional)
4991      */
4992     /**
4993      * @cfg {Number} md (Optional)
4994      */
4995     /**
4996      * @cfg {Number} lg (Optional)
4997      */
4998     /**
4999      * Returns the id of the column at the specified index.
5000      * @param {Number} index The column index
5001      * @return {String} the id
5002      */
5003     getColumnId : function(index){
5004         return this.config[index].id;
5005     },
5006
5007     /**
5008      * Returns the column for a specified id.
5009      * @param {String} id The column id
5010      * @return {Object} the column
5011      */
5012     getColumnById : function(id){
5013         return this.lookup[id];
5014     },
5015
5016     
5017     /**
5018      * Returns the column for a specified dataIndex.
5019      * @param {String} dataIndex The column dataIndex
5020      * @return {Object|Boolean} the column or false if not found
5021      */
5022     getColumnByDataIndex: function(dataIndex){
5023         var index = this.findColumnIndex(dataIndex);
5024         return index > -1 ? this.config[index] : false;
5025     },
5026     
5027     /**
5028      * Returns the index for a specified column id.
5029      * @param {String} id The column id
5030      * @return {Number} the index, or -1 if not found
5031      */
5032     getIndexById : function(id){
5033         for(var i = 0, len = this.config.length; i < len; i++){
5034             if(this.config[i].id == id){
5035                 return i;
5036             }
5037         }
5038         return -1;
5039     },
5040     
5041     /**
5042      * Returns the index for a specified column dataIndex.
5043      * @param {String} dataIndex The column dataIndex
5044      * @return {Number} the index, or -1 if not found
5045      */
5046     
5047     findColumnIndex : function(dataIndex){
5048         for(var i = 0, len = this.config.length; i < len; i++){
5049             if(this.config[i].dataIndex == dataIndex){
5050                 return i;
5051             }
5052         }
5053         return -1;
5054     },
5055     
5056     
5057     moveColumn : function(oldIndex, newIndex){
5058         var c = this.config[oldIndex];
5059         this.config.splice(oldIndex, 1);
5060         this.config.splice(newIndex, 0, c);
5061         this.dataMap = null;
5062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5063     },
5064
5065     isLocked : function(colIndex){
5066         return this.config[colIndex].locked === true;
5067     },
5068
5069     setLocked : function(colIndex, value, suppressEvent){
5070         if(this.isLocked(colIndex) == value){
5071             return;
5072         }
5073         this.config[colIndex].locked = value;
5074         if(!suppressEvent){
5075             this.fireEvent("columnlockchange", this, colIndex, value);
5076         }
5077     },
5078
5079     getTotalLockedWidth : function(){
5080         var totalWidth = 0;
5081         for(var i = 0; i < this.config.length; i++){
5082             if(this.isLocked(i) && !this.isHidden(i)){
5083                 this.totalWidth += this.getColumnWidth(i);
5084             }
5085         }
5086         return totalWidth;
5087     },
5088
5089     getLockedCount : function(){
5090         for(var i = 0, len = this.config.length; i < len; i++){
5091             if(!this.isLocked(i)){
5092                 return i;
5093             }
5094         }
5095     },
5096
5097     /**
5098      * Returns the number of columns.
5099      * @return {Number}
5100      */
5101     getColumnCount : function(visibleOnly){
5102         if(visibleOnly === true){
5103             var c = 0;
5104             for(var i = 0, len = this.config.length; i < len; i++){
5105                 if(!this.isHidden(i)){
5106                     c++;
5107                 }
5108             }
5109             return c;
5110         }
5111         return this.config.length;
5112     },
5113
5114     /**
5115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116      * @param {Function} fn
5117      * @param {Object} scope (optional)
5118      * @return {Array} result
5119      */
5120     getColumnsBy : function(fn, scope){
5121         var r = [];
5122         for(var i = 0, len = this.config.length; i < len; i++){
5123             var c = this.config[i];
5124             if(fn.call(scope||this, c, i) === true){
5125                 r[r.length] = c;
5126             }
5127         }
5128         return r;
5129     },
5130
5131     /**
5132      * Returns true if the specified column is sortable.
5133      * @param {Number} col The column index
5134      * @return {Boolean}
5135      */
5136     isSortable : function(col){
5137         if(typeof this.config[col].sortable == "undefined"){
5138             return this.defaultSortable;
5139         }
5140         return this.config[col].sortable;
5141     },
5142
5143     /**
5144      * Returns the rendering (formatting) function defined for the column.
5145      * @param {Number} col The column index.
5146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147      */
5148     getRenderer : function(col){
5149         if(!this.config[col].renderer){
5150             return Roo.grid.ColumnModel.defaultRenderer;
5151         }
5152         return this.config[col].renderer;
5153     },
5154
5155     /**
5156      * Sets the rendering (formatting) function for a column.
5157      * @param {Number} col The column index
5158      * @param {Function} fn The function to use to process the cell's raw data
5159      * to return HTML markup for the grid view. The render function is called with
5160      * the following parameters:<ul>
5161      * <li>Data value.</li>
5162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163      * <li>css A CSS style string to apply to the table cell.</li>
5164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166      * <li>Row index</li>
5167      * <li>Column index</li>
5168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169      */
5170     setRenderer : function(col, fn){
5171         this.config[col].renderer = fn;
5172     },
5173
5174     /**
5175      * Returns the width for the specified column.
5176      * @param {Number} col The column index
5177      * @return {Number}
5178      */
5179     getColumnWidth : function(col){
5180         return this.config[col].width * 1 || this.defaultWidth;
5181     },
5182
5183     /**
5184      * Sets the width for a column.
5185      * @param {Number} col The column index
5186      * @param {Number} width The new width
5187      */
5188     setColumnWidth : function(col, width, suppressEvent){
5189         this.config[col].width = width;
5190         this.totalWidth = null;
5191         if(!suppressEvent){
5192              this.fireEvent("widthchange", this, col, width);
5193         }
5194     },
5195
5196     /**
5197      * Returns the total width of all columns.
5198      * @param {Boolean} includeHidden True to include hidden column widths
5199      * @return {Number}
5200      */
5201     getTotalWidth : function(includeHidden){
5202         if(!this.totalWidth){
5203             this.totalWidth = 0;
5204             for(var i = 0, len = this.config.length; i < len; i++){
5205                 if(includeHidden || !this.isHidden(i)){
5206                     this.totalWidth += this.getColumnWidth(i);
5207                 }
5208             }
5209         }
5210         return this.totalWidth;
5211     },
5212
5213     /**
5214      * Returns the header for the specified column.
5215      * @param {Number} col The column index
5216      * @return {String}
5217      */
5218     getColumnHeader : function(col){
5219         return this.config[col].header;
5220     },
5221
5222     /**
5223      * Sets the header for a column.
5224      * @param {Number} col The column index
5225      * @param {String} header The new header
5226      */
5227     setColumnHeader : function(col, header){
5228         this.config[col].header = header;
5229         this.fireEvent("headerchange", this, col, header);
5230     },
5231
5232     /**
5233      * Returns the tooltip for the specified column.
5234      * @param {Number} col The column index
5235      * @return {String}
5236      */
5237     getColumnTooltip : function(col){
5238             return this.config[col].tooltip;
5239     },
5240     /**
5241      * Sets the tooltip for a column.
5242      * @param {Number} col The column index
5243      * @param {String} tooltip The new tooltip
5244      */
5245     setColumnTooltip : function(col, tooltip){
5246             this.config[col].tooltip = tooltip;
5247     },
5248
5249     /**
5250      * Returns the dataIndex for the specified column.
5251      * @param {Number} col The column index
5252      * @return {Number}
5253      */
5254     getDataIndex : function(col){
5255         return this.config[col].dataIndex;
5256     },
5257
5258     /**
5259      * Sets the dataIndex for a column.
5260      * @param {Number} col The column index
5261      * @param {Number} dataIndex The new dataIndex
5262      */
5263     setDataIndex : function(col, dataIndex){
5264         this.config[col].dataIndex = dataIndex;
5265     },
5266
5267     
5268     
5269     /**
5270      * Returns true if the cell is editable.
5271      * @param {Number} colIndex The column index
5272      * @param {Number} rowIndex The row index
5273      * @return {Boolean}
5274      */
5275     isCellEditable : function(colIndex, rowIndex){
5276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5277     },
5278
5279     /**
5280      * Returns the editor defined for the cell/column.
5281      * return false or null to disable editing.
5282      * @param {Number} colIndex The column index
5283      * @param {Number} rowIndex The row index
5284      * @return {Object}
5285      */
5286     getCellEditor : function(colIndex, rowIndex){
5287         return this.config[colIndex].editor;
5288     },
5289
5290     /**
5291      * Sets if a column is editable.
5292      * @param {Number} col The column index
5293      * @param {Boolean} editable True if the column is editable
5294      */
5295     setEditable : function(col, editable){
5296         this.config[col].editable = editable;
5297     },
5298
5299
5300     /**
5301      * Returns true if the column is hidden.
5302      * @param {Number} colIndex The column index
5303      * @return {Boolean}
5304      */
5305     isHidden : function(colIndex){
5306         return this.config[colIndex].hidden;
5307     },
5308
5309
5310     /**
5311      * Returns true if the column width cannot be changed
5312      */
5313     isFixed : function(colIndex){
5314         return this.config[colIndex].fixed;
5315     },
5316
5317     /**
5318      * Returns true if the column can be resized
5319      * @return {Boolean}
5320      */
5321     isResizable : function(colIndex){
5322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5323     },
5324     /**
5325      * Sets if a column is hidden.
5326      * @param {Number} colIndex The column index
5327      * @param {Boolean} hidden True if the column is hidden
5328      */
5329     setHidden : function(colIndex, hidden){
5330         this.config[colIndex].hidden = hidden;
5331         this.totalWidth = null;
5332         this.fireEvent("hiddenchange", this, colIndex, hidden);
5333     },
5334
5335     /**
5336      * Sets the editor for a column.
5337      * @param {Number} col The column index
5338      * @param {Object} editor The editor object
5339      */
5340     setEditor : function(col, editor){
5341         this.config[col].editor = editor;
5342     }
5343 });
5344
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346         if(typeof value == "string" && value.length < 1){
5347             return "&#160;";
5348         }
5349         return value;
5350 };
5351
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5354 /*
5355  * Based on:
5356  * Ext JS Library 1.1.1
5357  * Copyright(c) 2006-2007, Ext JS, LLC.
5358  *
5359  * Originally Released Under LGPL - original licence link has changed is not relivant.
5360  *
5361  * Fork - LGPL
5362  * <script type="text/javascript">
5363  */
5364  
5365 /**
5366  * @class Roo.LoadMask
5367  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5368  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5370  * element's UpdateManager load indicator and will be destroyed after the initial load.
5371  * @constructor
5372  * Create a new LoadMask
5373  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374  * @param {Object} config The config object
5375  */
5376 Roo.LoadMask = function(el, config){
5377     this.el = Roo.get(el);
5378     Roo.apply(this, config);
5379     if(this.store){
5380         this.store.on('beforeload', this.onBeforeLoad, this);
5381         this.store.on('load', this.onLoad, this);
5382         this.store.on('loadexception', this.onLoadException, this);
5383         this.removeMask = false;
5384     }else{
5385         var um = this.el.getUpdateManager();
5386         um.showLoadIndicator = false; // disable the default indicator
5387         um.on('beforeupdate', this.onBeforeLoad, this);
5388         um.on('update', this.onLoad, this);
5389         um.on('failure', this.onLoad, this);
5390         this.removeMask = true;
5391     }
5392 };
5393
5394 Roo.LoadMask.prototype = {
5395     /**
5396      * @cfg {Boolean} removeMask
5397      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5399      */
5400     /**
5401      * @cfg {String} msg
5402      * The text to display in a centered loading message box (defaults to 'Loading...')
5403      */
5404     msg : 'Loading...',
5405     /**
5406      * @cfg {String} msgCls
5407      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408      */
5409     msgCls : 'x-mask-loading',
5410
5411     /**
5412      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5413      * @type Boolean
5414      */
5415     disabled: false,
5416
5417     /**
5418      * Disables the mask to prevent it from being displayed
5419      */
5420     disable : function(){
5421        this.disabled = true;
5422     },
5423
5424     /**
5425      * Enables the mask so that it can be displayed
5426      */
5427     enable : function(){
5428         this.disabled = false;
5429     },
5430     
5431     onLoadException : function()
5432     {
5433         Roo.log(arguments);
5434         
5435         if (typeof(arguments[3]) != 'undefined') {
5436             Roo.MessageBox.alert("Error loading",arguments[3]);
5437         } 
5438         /*
5439         try {
5440             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5442             }   
5443         } catch(e) {
5444             
5445         }
5446         */
5447     
5448         
5449         
5450         this.el.unmask(this.removeMask);
5451     },
5452     // private
5453     onLoad : function()
5454     {
5455         this.el.unmask(this.removeMask);
5456     },
5457
5458     // private
5459     onBeforeLoad : function(){
5460         if(!this.disabled){
5461             this.el.mask(this.msg, this.msgCls);
5462         }
5463     },
5464
5465     // private
5466     destroy : function(){
5467         if(this.store){
5468             this.store.un('beforeload', this.onBeforeLoad, this);
5469             this.store.un('load', this.onLoad, this);
5470             this.store.un('loadexception', this.onLoadException, this);
5471         }else{
5472             var um = this.el.getUpdateManager();
5473             um.un('beforeupdate', this.onBeforeLoad, this);
5474             um.un('update', this.onLoad, this);
5475             um.un('failure', this.onLoad, this);
5476         }
5477     }
5478 };/*
5479  * - LGPL
5480  *
5481  * table
5482  * 
5483  */
5484
5485 /**
5486  * @class Roo.bootstrap.Table
5487  * @extends Roo.bootstrap.Component
5488  * Bootstrap Table class
5489  * @cfg {String} cls table class
5490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491  * @cfg {String} bgcolor Specifies the background color for a table
5492  * @cfg {Number} border Specifies whether the table cells should have borders or not
5493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494  * @cfg {Number} cellspacing Specifies the space between cells
5495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497  * @cfg {String} sortable Specifies that the table should be sortable
5498  * @cfg {String} summary Specifies a summary of the content of a table
5499  * @cfg {Number} width Specifies the width of a table
5500  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501  * 
5502  * @cfg {boolean} striped Should the rows be alternative striped
5503  * @cfg {boolean} bordered Add borders to the table
5504  * @cfg {boolean} hover Add hover highlighting
5505  * @cfg {boolean} condensed Format condensed
5506  * @cfg {boolean} responsive Format condensed
5507  * @cfg {Boolean} loadMask (true|false) default false
5508  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510  * @cfg {Boolean} rowSelection (true|false) default false
5511  * @cfg {Boolean} cellSelection (true|false) default false
5512  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5513  
5514  * 
5515  * @constructor
5516  * Create a new Table
5517  * @param {Object} config The config object
5518  */
5519
5520 Roo.bootstrap.Table = function(config){
5521     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5522     
5523     // BC...
5524     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5528     
5529     
5530     if (this.sm) {
5531         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532         this.sm = this.selModel;
5533         this.sm.xmodule = this.xmodule || false;
5534     }
5535     if (this.cm && typeof(this.cm.config) == 'undefined') {
5536         this.colModel = new Roo.grid.ColumnModel(this.cm);
5537         this.cm = this.colModel;
5538         this.cm.xmodule = this.xmodule || false;
5539     }
5540     if (this.store) {
5541         this.store= Roo.factory(this.store, Roo.data);
5542         this.ds = this.store;
5543         this.ds.xmodule = this.xmodule || false;
5544          
5545     }
5546     if (this.footer && this.store) {
5547         this.footer.dataSource = this.ds;
5548         this.footer = Roo.factory(this.footer);
5549     }
5550     
5551     /** @private */
5552     this.addEvents({
5553         /**
5554          * @event cellclick
5555          * Fires when a cell is clicked
5556          * @param {Roo.bootstrap.Table} this
5557          * @param {Roo.Element} el
5558          * @param {Number} rowIndex
5559          * @param {Number} columnIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "cellclick" : true,
5563         /**
5564          * @event celldblclick
5565          * Fires when a cell is double clicked
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "celldblclick" : true,
5573         /**
5574          * @event rowclick
5575          * Fires when a row is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Roo.EventObject} e
5580          */
5581         "rowclick" : true,
5582         /**
5583          * @event rowdblclick
5584          * Fires when a row is double clicked
5585          * @param {Roo.bootstrap.Table} this
5586          * @param {Roo.Element} el
5587          * @param {Number} rowIndex
5588          * @param {Roo.EventObject} e
5589          */
5590         "rowdblclick" : true,
5591         /**
5592          * @event mouseover
5593          * Fires when a mouseover occur
5594          * @param {Roo.bootstrap.Table} this
5595          * @param {Roo.Element} el
5596          * @param {Number} rowIndex
5597          * @param {Number} columnIndex
5598          * @param {Roo.EventObject} e
5599          */
5600         "mouseover" : true,
5601         /**
5602          * @event mouseout
5603          * Fires when a mouseout occur
5604          * @param {Roo.bootstrap.Table} this
5605          * @param {Roo.Element} el
5606          * @param {Number} rowIndex
5607          * @param {Number} columnIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "mouseout" : true,
5611         /**
5612          * @event rowclass
5613          * Fires when a row is rendered, so you can change add a style to it.
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5616          */
5617         'rowclass' : true,
5618           /**
5619          * @event rowsrendered
5620          * Fires when all the  rows have been rendered
5621          * @param {Roo.bootstrap.Table} this
5622          */
5623         'rowsrendered' : true
5624         
5625     });
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     align: false,
5632     bgcolor: false,
5633     border: false,
5634     cellpadding: false,
5635     cellspacing: false,
5636     frame: false,
5637     rules: false,
5638     sortable: false,
5639     summary: false,
5640     width: false,
5641     striped : false,
5642     bordered: false,
5643     hover:  false,
5644     condensed : false,
5645     responsive : false,
5646     sm : false,
5647     cm : false,
5648     store : false,
5649     loadMask : false,
5650     footerShow : true,
5651     headerShow : true,
5652   
5653     rowSelection : false,
5654     cellSelection : false,
5655     layout : false,
5656     
5657     // Roo.Element - the tbody
5658     mainBody: false, 
5659     
5660     getAutoCreate : function(){
5661         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5662         
5663         cfg = {
5664             tag: 'table',
5665             cls : 'table',
5666             cn : []
5667         };
5668             
5669         if (this.striped) {
5670             cfg.cls += ' table-striped';
5671         }
5672         
5673         if (this.hover) {
5674             cfg.cls += ' table-hover';
5675         }
5676         if (this.bordered) {
5677             cfg.cls += ' table-bordered';
5678         }
5679         if (this.condensed) {
5680             cfg.cls += ' table-condensed';
5681         }
5682         if (this.responsive) {
5683             cfg.cls += ' table-responsive';
5684         }
5685         
5686         if (this.cls) {
5687             cfg.cls+=  ' ' +this.cls;
5688         }
5689         
5690         // this lot should be simplifed...
5691         
5692         if (this.align) {
5693             cfg.align=this.align;
5694         }
5695         if (this.bgcolor) {
5696             cfg.bgcolor=this.bgcolor;
5697         }
5698         if (this.border) {
5699             cfg.border=this.border;
5700         }
5701         if (this.cellpadding) {
5702             cfg.cellpadding=this.cellpadding;
5703         }
5704         if (this.cellspacing) {
5705             cfg.cellspacing=this.cellspacing;
5706         }
5707         if (this.frame) {
5708             cfg.frame=this.frame;
5709         }
5710         if (this.rules) {
5711             cfg.rules=this.rules;
5712         }
5713         if (this.sortable) {
5714             cfg.sortable=this.sortable;
5715         }
5716         if (this.summary) {
5717             cfg.summary=this.summary;
5718         }
5719         if (this.width) {
5720             cfg.width=this.width;
5721         }
5722         if (this.layout) {
5723             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5724         }
5725         
5726         if(this.store || this.cm){
5727             if(this.headerShow){
5728                 cfg.cn.push(this.renderHeader());
5729             }
5730             
5731             cfg.cn.push(this.renderBody());
5732             
5733             if(this.footerShow){
5734                 cfg.cn.push(this.renderFooter());
5735             }
5736             
5737             cfg.cls+=  ' TableGrid';
5738         }
5739         
5740         return { cn : [ cfg ] };
5741     },
5742     
5743     initEvents : function()
5744     {   
5745         if(!this.store || !this.cm){
5746             return;
5747         }
5748         
5749         //Roo.log('initEvents with ds!!!!');
5750         
5751         this.mainBody = this.el.select('tbody', true).first();
5752         
5753         
5754         var _this = this;
5755         
5756         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757             e.on('click', _this.sort, _this);
5758         });
5759         
5760         this.el.on("click", this.onClick, this);
5761         this.el.on("dblclick", this.onDblClick, this);
5762         
5763         // why is this done????? = it breaks dialogs??
5764         //this.parent().el.setStyle('position', 'relative');
5765         
5766         
5767         if (this.footer) {
5768             this.footer.parentId = this.id;
5769             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5770         }
5771         
5772         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773         
5774         this.store.on('load', this.onLoad, this);
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('update', this.onUpdate, this);
5777         this.store.on('add', this.onAdd, this);
5778         
5779     },
5780     
5781     onMouseover : function(e, el)
5782     {
5783         var cell = Roo.get(el);
5784         
5785         if(!cell){
5786             return;
5787         }
5788         
5789         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790             cell = cell.findParent('td', false, true);
5791         }
5792         
5793         var row = cell.findParent('tr', false, true);
5794         var cellIndex = cell.dom.cellIndex;
5795         var rowIndex = row.dom.rowIndex - 1; // start from 0
5796         
5797         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5798         
5799     },
5800     
5801     onMouseout : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onClick : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell || (!this.cellSelection && !this.rowSelection)){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         if(!cell || typeof(cell) == 'undefined'){
5834             return;
5835         }
5836         
5837         var row = cell.findParent('tr', false, true);
5838         
5839         if(!row || typeof(row) == 'undefined'){
5840             return;
5841         }
5842         
5843         var cellIndex = cell.dom.cellIndex;
5844         var rowIndex = this.getRowIndex(row);
5845         
5846         // why??? - should these not be based on SelectionModel?
5847         if(this.cellSelection){
5848             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5849         }
5850         
5851         if(this.rowSelection){
5852             this.fireEvent('rowclick', this, row, rowIndex, e);
5853         }
5854         
5855         
5856     },
5857     
5858     onDblClick : function(e,el)
5859     {
5860         var cell = Roo.get(el);
5861         
5862         if(!cell || (!this.CellSelection && !this.RowSelection)){
5863             return;
5864         }
5865         
5866         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867             cell = cell.findParent('td', false, true);
5868         }
5869         
5870         if(!cell || typeof(cell) == 'undefined'){
5871             return;
5872         }
5873         
5874         var row = cell.findParent('tr', false, true);
5875         
5876         if(!row || typeof(row) == 'undefined'){
5877             return;
5878         }
5879         
5880         var cellIndex = cell.dom.cellIndex;
5881         var rowIndex = this.getRowIndex(row);
5882         
5883         if(this.CellSelection){
5884             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5885         }
5886         
5887         if(this.RowSelection){
5888             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5889         }
5890     },
5891     
5892     sort : function(e,el)
5893     {
5894         var col = Roo.get(el);
5895         
5896         if(!col.hasClass('sortable')){
5897             return;
5898         }
5899         
5900         var sort = col.attr('sort');
5901         var dir = 'ASC';
5902         
5903         if(col.hasClass('glyphicon-arrow-up')){
5904             dir = 'DESC';
5905         }
5906         
5907         this.store.sortInfo = {field : sort, direction : dir};
5908         
5909         if (this.footer) {
5910             Roo.log("calling footer first");
5911             this.footer.onClick('first');
5912         } else {
5913         
5914             this.store.load({ params : { start : 0 } });
5915         }
5916     },
5917     
5918     renderHeader : function()
5919     {
5920         var header = {
5921             tag: 'thead',
5922             cn : []
5923         };
5924         
5925         var cm = this.cm;
5926         
5927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928             
5929             var config = cm.config[i];
5930             
5931             var c = {
5932                 tag: 'th',
5933                 style : '',
5934                 html: cm.getColumnHeader(i)
5935             };
5936             
5937             var hh = '';
5938             
5939             if(typeof(config.lgHeader) != 'undefined'){
5940                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5941             }
5942             
5943             if(typeof(config.mdHeader) != 'undefined'){
5944                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5945             }
5946             
5947             if(typeof(config.smHeader) != 'undefined'){
5948                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5949             }
5950             
5951             if(typeof(config.xsHeader) != 'undefined'){
5952                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5953             }
5954             
5955             if(hh.length){
5956                 c.html = hh;
5957             }
5958             
5959             if(typeof(config.tooltip) != 'undefined'){
5960                 c.tooltip = config.tooltip;
5961             }
5962             
5963             if(typeof(config.colspan) != 'undefined'){
5964                 c.colspan = config.colspan;
5965             }
5966             
5967             if(typeof(config.hidden) != 'undefined' && config.hidden){
5968                 c.style += ' display:none;';
5969             }
5970             
5971             if(typeof(config.dataIndex) != 'undefined'){
5972                 c.sort = config.dataIndex;
5973             }
5974             
5975             if(typeof(config.sortable) != 'undefined' && config.sortable){
5976                 c.cls = 'sortable';
5977             }
5978             
5979             if(typeof(config.align) != 'undefined' && config.align.length){
5980                 c.style += ' text-align:' + config.align + ';';
5981             }
5982             
5983             if(typeof(config.width) != 'undefined'){
5984                 c.style += ' width:' + config.width + 'px;';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5989             }
5990             
5991             ['xs','sm','md','lg'].map(function(size){
5992                 
5993                 if(typeof(config[size]) == 'undefined'){
5994                     return;
5995                 }
5996                 
5997                 if (!config[size]) { // 0 = hidden
5998                     cfg.cls += ' hidden-' + size;
5999                     return;
6000                 }
6001                 
6002                 cfg.cls += ' col-' + size + '-' + config[size];
6003
6004             });
6005             
6006             header.cn.push(c)
6007         }
6008         
6009         return header;
6010     },
6011     
6012     renderBody : function()
6013     {
6014         var body = {
6015             tag: 'tbody',
6016             cn : [
6017                 {
6018                     tag: 'tr',
6019                     cn : [
6020                         {
6021                             tag : 'td',
6022                             colspan :  this.cm.getColumnCount()
6023                         }
6024                     ]
6025                 }
6026             ]
6027         };
6028         
6029         return body;
6030     },
6031     
6032     renderFooter : function()
6033     {
6034         var footer = {
6035             tag: 'tfoot',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return footer;
6050     },
6051     
6052     
6053     
6054     onLoad : function()
6055     {
6056         Roo.log('ds onload');
6057         this.clear();
6058         
6059         var _this = this;
6060         var cm = this.cm;
6061         var ds = this.store;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065             
6066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6068             }
6069             
6070             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6072             }
6073         });
6074         
6075         var tbody =  this.mainBody;
6076               
6077         if(ds.getCount() > 0){
6078             ds.data.each(function(d,rowIndex){
6079                 var row =  this.renderRow(cm, ds, rowIndex);
6080                 
6081                 tbody.createChild(row);
6082                 
6083                 var _this = this;
6084                 
6085                 if(row.cellObjects.length){
6086                     Roo.each(row.cellObjects, function(r){
6087                         _this.renderCellObject(r);
6088                     })
6089                 }
6090                 
6091             }, this);
6092         }
6093         
6094         Roo.each(this.el.select('tbody td', true).elements, function(e){
6095             e.on('mouseover', _this.onMouseover, _this);
6096         });
6097         
6098         Roo.each(this.el.select('tbody td', true).elements, function(e){
6099             e.on('mouseout', _this.onMouseout, _this);
6100         });
6101         this.fireEvent('rowsrendered', this);
6102         //if(this.loadMask){
6103         //    this.maskEl.hide();
6104         //}
6105     },
6106     
6107     
6108     onUpdate : function(ds,record)
6109     {
6110         this.refreshRow(record);
6111     },
6112     
6113     onRemove : function(ds, record, index, isUpdate){
6114         if(isUpdate !== true){
6115             this.fireEvent("beforerowremoved", this, index, record);
6116         }
6117         var bt = this.mainBody.dom;
6118         
6119         var rows = this.el.select('tbody > tr', true).elements;
6120         
6121         if(typeof(rows[index]) != 'undefined'){
6122             bt.removeChild(rows[index].dom);
6123         }
6124         
6125 //        if(bt.rows[index]){
6126 //            bt.removeChild(bt.rows[index]);
6127 //        }
6128         
6129         if(isUpdate !== true){
6130             //this.stripeRows(index);
6131             //this.syncRowHeights(index, index);
6132             //this.layout();
6133             this.fireEvent("rowremoved", this, index, record);
6134         }
6135     },
6136     
6137     onAdd : function(ds, records, rowIndex)
6138     {
6139         //Roo.log('on Add called');
6140         // - note this does not handle multiple adding very well..
6141         var bt = this.mainBody.dom;
6142         for (var i =0 ; i < records.length;i++) {
6143             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144             //Roo.log(records[i]);
6145             //Roo.log(this.store.getAt(rowIndex+i));
6146             this.insertRow(this.store, rowIndex + i, false);
6147             return;
6148         }
6149         
6150     },
6151     
6152     
6153     refreshRow : function(record){
6154         var ds = this.store, index;
6155         if(typeof record == 'number'){
6156             index = record;
6157             record = ds.getAt(index);
6158         }else{
6159             index = ds.indexOf(record);
6160         }
6161         this.insertRow(ds, index, true);
6162         this.onRemove(ds, record, index+1, true);
6163         //this.syncRowHeights(index, index);
6164         //this.layout();
6165         this.fireEvent("rowupdated", this, index, record);
6166     },
6167     
6168     insertRow : function(dm, rowIndex, isUpdate){
6169         
6170         if(!isUpdate){
6171             this.fireEvent("beforerowsinserted", this, rowIndex);
6172         }
6173             //var s = this.getScrollState();
6174         var row = this.renderRow(this.cm, this.store, rowIndex);
6175         // insert before rowIndex..
6176         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6177         
6178         var _this = this;
6179                 
6180         if(row.cellObjects.length){
6181             Roo.each(row.cellObjects, function(r){
6182                 _this.renderCellObject(r);
6183             })
6184         }
6185             
6186         if(!isUpdate){
6187             this.fireEvent("rowsinserted", this, rowIndex);
6188             //this.syncRowHeights(firstRow, lastRow);
6189             //this.stripeRows(firstRow);
6190             //this.layout();
6191         }
6192         
6193     },
6194     
6195     
6196     getRowDom : function(rowIndex)
6197     {
6198         var rows = this.el.select('tbody > tr', true).elements;
6199         
6200         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6201         
6202     },
6203     // returns the object tree for a tr..
6204   
6205     
6206     renderRow : function(cm, ds, rowIndex) 
6207     {
6208         
6209         var d = ds.getAt(rowIndex);
6210         
6211         var row = {
6212             tag : 'tr',
6213             cn : []
6214         };
6215             
6216         var cellObjects = [];
6217         
6218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219             var config = cm.config[i];
6220             
6221             var renderer = cm.getRenderer(i);
6222             var value = '';
6223             var id = false;
6224             
6225             if(typeof(renderer) !== 'undefined'){
6226                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227             }
6228             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229             // and are rendered into the cells after the row is rendered - using the id for the element.
6230             
6231             if(typeof(value) === 'object'){
6232                 id = Roo.id();
6233                 cellObjects.push({
6234                     container : id,
6235                     cfg : value 
6236                 })
6237             }
6238             
6239             var rowcfg = {
6240                 record: d,
6241                 rowIndex : rowIndex,
6242                 colIndex : i,
6243                 rowClass : ''
6244             };
6245
6246             this.fireEvent('rowclass', this, rowcfg);
6247             
6248             var td = {
6249                 tag: 'td',
6250                 cls : rowcfg.rowClass,
6251                 style: '',
6252                 html: (typeof(value) === 'object') ? '' : value
6253             };
6254             
6255             if (id) {
6256                 td.id = id;
6257             }
6258             
6259             if(typeof(config.colspan) != 'undefined'){
6260                 td.colspan = config.colspan;
6261             }
6262             
6263             if(typeof(config.hidden) != 'undefined' && config.hidden){
6264                 td.style += ' display:none;';
6265             }
6266             
6267             if(typeof(config.align) != 'undefined' && config.align.length){
6268                 td.style += ' text-align:' + config.align + ';';
6269             }
6270             
6271             if(typeof(config.width) != 'undefined'){
6272                 td.style += ' width:' +  config.width + 'px;';
6273             }
6274             
6275             if(typeof(config.cursor) != 'undefined'){
6276                 td.style += ' cursor:' +  config.cursor + ';';
6277             }
6278             
6279             if(typeof(config.cls) != 'undefined'){
6280                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6281             }
6282             
6283             ['xs','sm','md','lg'].map(function(size){
6284                 
6285                 if(typeof(config[size]) == 'undefined'){
6286                     return;
6287                 }
6288                 
6289                 if (!config[size]) { // 0 = hidden
6290                     td.cls += ' hidden-' + size;
6291                     return;
6292                 }
6293                 
6294                 td.cls += ' col-' + size + '-' + config[size];
6295
6296             });
6297              
6298             row.cn.push(td);
6299            
6300         }
6301         
6302         row.cellObjects = cellObjects;
6303         
6304         return row;
6305           
6306     },
6307     
6308     
6309     
6310     onBeforeLoad : function()
6311     {
6312         //Roo.log('ds onBeforeLoad');
6313         
6314         //this.clear();
6315         
6316         //if(this.loadMask){
6317         //    this.maskEl.show();
6318         //}
6319     },
6320      /**
6321      * Remove all rows
6322      */
6323     clear : function()
6324     {
6325         this.el.select('tbody', true).first().dom.innerHTML = '';
6326     },
6327     /**
6328      * Show or hide a row.
6329      * @param {Number} rowIndex to show or hide
6330      * @param {Boolean} state hide
6331      */
6332     setRowVisibility : function(rowIndex, state)
6333     {
6334         var bt = this.mainBody.dom;
6335         
6336         var rows = this.el.select('tbody > tr', true).elements;
6337         
6338         if(typeof(rows[rowIndex]) == 'undefined'){
6339             return;
6340         }
6341         rows[rowIndex].dom.style.display = state ? '' : 'none';
6342     },
6343     
6344     
6345     getSelectionModel : function(){
6346         if(!this.selModel){
6347             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348         }
6349         return this.selModel;
6350     },
6351     /*
6352      * Render the Roo.bootstrap object from renderder
6353      */
6354     renderCellObject : function(r)
6355     {
6356         var _this = this;
6357         
6358         var t = r.cfg.render(r.container);
6359         
6360         if(r.cfg.cn){
6361             Roo.each(r.cfg.cn, function(c){
6362                 var child = {
6363                     container: t.getChildContainer(),
6364                     cfg: c
6365                 };
6366                 _this.renderCellObject(child);
6367             })
6368         }
6369     },
6370     
6371     getRowIndex : function(row)
6372     {
6373         var rowIndex = -1;
6374         
6375         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6376             if(el != row){
6377                 return;
6378             }
6379             
6380             rowIndex = index;
6381         });
6382         
6383         return rowIndex;
6384     }
6385    
6386 });
6387
6388  
6389
6390  /*
6391  * - LGPL
6392  *
6393  * table cell
6394  * 
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.TableCell
6399  * @extends Roo.bootstrap.Component
6400  * Bootstrap TableCell class
6401  * @cfg {String} html cell contain text
6402  * @cfg {String} cls cell class
6403  * @cfg {String} tag cell tag (td|th) default td
6404  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405  * @cfg {String} align Aligns the content in a cell
6406  * @cfg {String} axis Categorizes cells
6407  * @cfg {String} bgcolor Specifies the background color of a cell
6408  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409  * @cfg {Number} colspan Specifies the number of columns a cell should span
6410  * @cfg {String} headers Specifies one or more header cells a cell is related to
6411  * @cfg {Number} height Sets the height of a cell
6412  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413  * @cfg {Number} rowspan Sets the number of rows a cell should span
6414  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415  * @cfg {String} valign Vertical aligns the content in a cell
6416  * @cfg {Number} width Specifies the width of a cell
6417  * 
6418  * @constructor
6419  * Create a new TableCell
6420  * @param {Object} config The config object
6421  */
6422
6423 Roo.bootstrap.TableCell = function(config){
6424     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6425 };
6426
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6428     
6429     html: false,
6430     cls: false,
6431     tag: false,
6432     abbr: false,
6433     align: false,
6434     axis: false,
6435     bgcolor: false,
6436     charoff: false,
6437     colspan: false,
6438     headers: false,
6439     height: false,
6440     nowrap: false,
6441     rowspan: false,
6442     scope: false,
6443     valign: false,
6444     width: false,
6445     
6446     
6447     getAutoCreate : function(){
6448         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6449         
6450         cfg = {
6451             tag: 'td'
6452         };
6453         
6454         if(this.tag){
6455             cfg.tag = this.tag;
6456         }
6457         
6458         if (this.html) {
6459             cfg.html=this.html
6460         }
6461         if (this.cls) {
6462             cfg.cls=this.cls
6463         }
6464         if (this.abbr) {
6465             cfg.abbr=this.abbr
6466         }
6467         if (this.align) {
6468             cfg.align=this.align
6469         }
6470         if (this.axis) {
6471             cfg.axis=this.axis
6472         }
6473         if (this.bgcolor) {
6474             cfg.bgcolor=this.bgcolor
6475         }
6476         if (this.charoff) {
6477             cfg.charoff=this.charoff
6478         }
6479         if (this.colspan) {
6480             cfg.colspan=this.colspan
6481         }
6482         if (this.headers) {
6483             cfg.headers=this.headers
6484         }
6485         if (this.height) {
6486             cfg.height=this.height
6487         }
6488         if (this.nowrap) {
6489             cfg.nowrap=this.nowrap
6490         }
6491         if (this.rowspan) {
6492             cfg.rowspan=this.rowspan
6493         }
6494         if (this.scope) {
6495             cfg.scope=this.scope
6496         }
6497         if (this.valign) {
6498             cfg.valign=this.valign
6499         }
6500         if (this.width) {
6501             cfg.width=this.width
6502         }
6503         
6504         
6505         return cfg;
6506     }
6507    
6508 });
6509
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * table row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.TableRow
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap TableRow class
6523  * @cfg {String} cls row class
6524  * @cfg {String} align Aligns the content in a table row
6525  * @cfg {String} bgcolor Specifies a background color for a table row
6526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527  * @cfg {String} valign Vertical aligns the content in a table row
6528  * 
6529  * @constructor
6530  * Create a new TableRow
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableRow = function(config){
6535     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6539     
6540     cls: false,
6541     align: false,
6542     bgcolor: false,
6543     charoff: false,
6544     valign: false,
6545     
6546     getAutoCreate : function(){
6547         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6548         
6549         cfg = {
6550             tag: 'tr'
6551         };
6552             
6553         if(this.cls){
6554             cfg.cls = this.cls;
6555         }
6556         if(this.align){
6557             cfg.align = this.align;
6558         }
6559         if(this.bgcolor){
6560             cfg.bgcolor = this.bgcolor;
6561         }
6562         if(this.charoff){
6563             cfg.charoff = this.charoff;
6564         }
6565         if(this.valign){
6566             cfg.valign = this.valign;
6567         }
6568         
6569         return cfg;
6570     }
6571    
6572 });
6573
6574  
6575
6576  /*
6577  * - LGPL
6578  *
6579  * table body
6580  * 
6581  */
6582
6583 /**
6584  * @class Roo.bootstrap.TableBody
6585  * @extends Roo.bootstrap.Component
6586  * Bootstrap TableBody class
6587  * @cfg {String} cls element class
6588  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589  * @cfg {String} align Aligns the content inside the element
6590  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6592  * 
6593  * @constructor
6594  * Create a new TableBody
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.TableBody = function(config){
6599     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     tag: false,
6606     align: false,
6607     charoff: false,
6608     valign: false,
6609     
6610     getAutoCreate : function(){
6611         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6612         
6613         cfg = {
6614             tag: 'tbody'
6615         };
6616             
6617         if (this.cls) {
6618             cfg.cls=this.cls
6619         }
6620         if(this.tag){
6621             cfg.tag = this.tag;
6622         }
6623         
6624         if(this.align){
6625             cfg.align = this.align;
6626         }
6627         if(this.charoff){
6628             cfg.charoff = this.charoff;
6629         }
6630         if(this.valign){
6631             cfg.valign = this.valign;
6632         }
6633         
6634         return cfg;
6635     }
6636     
6637     
6638 //    initEvents : function()
6639 //    {
6640 //        
6641 //        if(!this.store){
6642 //            return;
6643 //        }
6644 //        
6645 //        this.store = Roo.factory(this.store, Roo.data);
6646 //        this.store.on('load', this.onLoad, this);
6647 //        
6648 //        this.store.load();
6649 //        
6650 //    },
6651 //    
6652 //    onLoad: function () 
6653 //    {   
6654 //        this.fireEvent('load', this);
6655 //    }
6656 //    
6657 //   
6658 });
6659
6660  
6661
6662  /*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6675  /**
6676  * @class Roo.form.Action
6677  * Internal Class used to handle form actions
6678  * @constructor
6679  * @param {Roo.form.BasicForm} el The form element or its id
6680  * @param {Object} config Configuration options
6681  */
6682
6683  
6684  
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6687     this.form = form;
6688     this.options = options || {};
6689 };
6690 /**
6691  * Client Validation Failed
6692  * @const 
6693  */
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6695 /**
6696  * Server Validation Failed
6697  * @const 
6698  */
6699 Roo.form.Action.SERVER_INVALID = 'server';
6700  /**
6701  * Connect to Server Failed
6702  * @const 
6703  */
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 /**
6706  * Reading Data from Server Failed
6707  * @const 
6708  */
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6710
6711 Roo.form.Action.prototype = {
6712     type : 'default',
6713     failureType : undefined,
6714     response : undefined,
6715     result : undefined,
6716
6717     // interface method
6718     run : function(options){
6719
6720     },
6721
6722     // interface method
6723     success : function(response){
6724
6725     },
6726
6727     // interface method
6728     handleResponse : function(response){
6729
6730     },
6731
6732     // default connection failure
6733     failure : function(response){
6734         
6735         this.response = response;
6736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737         this.form.afterAction(this, false);
6738     },
6739
6740     processResponse : function(response){
6741         this.response = response;
6742         if(!response.responseText){
6743             return true;
6744         }
6745         this.result = this.handleResponse(response);
6746         return this.result;
6747     },
6748
6749     // utility functions used internally
6750     getUrl : function(appendParams){
6751         var url = this.options.url || this.form.url || this.form.el.dom.action;
6752         if(appendParams){
6753             var p = this.getParams();
6754             if(p){
6755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6756             }
6757         }
6758         return url;
6759     },
6760
6761     getMethod : function(){
6762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6763     },
6764
6765     getParams : function(){
6766         var bp = this.form.baseParams;
6767         var p = this.options.params;
6768         if(p){
6769             if(typeof p == "object"){
6770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771             }else if(typeof p == 'string' && bp){
6772                 p += '&' + Roo.urlEncode(bp);
6773             }
6774         }else if(bp){
6775             p = Roo.urlEncode(bp);
6776         }
6777         return p;
6778     },
6779
6780     createCallback : function(){
6781         return {
6782             success: this.success,
6783             failure: this.failure,
6784             scope: this,
6785             timeout: (this.form.timeout*1000),
6786             upload: this.form.fileUpload ? this.success : undefined
6787         };
6788     }
6789 };
6790
6791 Roo.form.Action.Submit = function(form, options){
6792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6793 };
6794
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6796     type : 'submit',
6797
6798     haveProgress : false,
6799     uploadComplete : false,
6800     
6801     // uploadProgress indicator.
6802     uploadProgress : function()
6803     {
6804         if (!this.form.progressUrl) {
6805             return;
6806         }
6807         
6808         if (!this.haveProgress) {
6809             Roo.MessageBox.progress("Uploading", "Uploading");
6810         }
6811         if (this.uploadComplete) {
6812            Roo.MessageBox.hide();
6813            return;
6814         }
6815         
6816         this.haveProgress = true;
6817    
6818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819         
6820         var c = new Roo.data.Connection();
6821         c.request({
6822             url : this.form.progressUrl,
6823             params: {
6824                 id : uid
6825             },
6826             method: 'GET',
6827             success : function(req){
6828                //console.log(data);
6829                 var rdata = false;
6830                 var edata;
6831                 try  {
6832                    rdata = Roo.decode(req.responseText)
6833                 } catch (e) {
6834                     Roo.log("Invalid data from server..");
6835                     Roo.log(edata);
6836                     return;
6837                 }
6838                 if (!rdata || !rdata.success) {
6839                     Roo.log(rdata);
6840                     Roo.MessageBox.alert(Roo.encode(rdata));
6841                     return;
6842                 }
6843                 var data = rdata.data;
6844                 
6845                 if (this.uploadComplete) {
6846                    Roo.MessageBox.hide();
6847                    return;
6848                 }
6849                    
6850                 if (data){
6851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6853                     );
6854                 }
6855                 this.uploadProgress.defer(2000,this);
6856             },
6857        
6858             failure: function(data) {
6859                 Roo.log('progress url failed ');
6860                 Roo.log(data);
6861             },
6862             scope : this
6863         });
6864            
6865     },
6866     
6867     
6868     run : function()
6869     {
6870         // run get Values on the form, so it syncs any secondary forms.
6871         this.form.getValues();
6872         
6873         var o = this.options;
6874         var method = this.getMethod();
6875         var isPost = method == 'POST';
6876         if(o.clientValidation === false || this.form.isValid()){
6877             
6878             if (this.form.progressUrl) {
6879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880                     (new Date() * 1) + '' + Math.random());
6881                     
6882             } 
6883             
6884             
6885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886                 form:this.form.el.dom,
6887                 url:this.getUrl(!isPost),
6888                 method: method,
6889                 params:isPost ? this.getParams() : null,
6890                 isUpload: this.form.fileUpload
6891             }));
6892             
6893             this.uploadProgress();
6894
6895         }else if (o.clientValidation !== false){ // client validation failed
6896             this.failureType = Roo.form.Action.CLIENT_INVALID;
6897             this.form.afterAction(this, false);
6898         }
6899     },
6900
6901     success : function(response)
6902     {
6903         this.uploadComplete= true;
6904         if (this.haveProgress) {
6905             Roo.MessageBox.hide();
6906         }
6907         
6908         
6909         var result = this.processResponse(response);
6910         if(result === true || result.success){
6911             this.form.afterAction(this, true);
6912             return;
6913         }
6914         if(result.errors){
6915             this.form.markInvalid(result.errors);
6916             this.failureType = Roo.form.Action.SERVER_INVALID;
6917         }
6918         this.form.afterAction(this, false);
6919     },
6920     failure : function(response)
6921     {
6922         this.uploadComplete= true;
6923         if (this.haveProgress) {
6924             Roo.MessageBox.hide();
6925         }
6926         
6927         this.response = response;
6928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929         this.form.afterAction(this, false);
6930     },
6931     
6932     handleResponse : function(response){
6933         if(this.form.errorReader){
6934             var rs = this.form.errorReader.read(response);
6935             var errors = [];
6936             if(rs.records){
6937                 for(var i = 0, len = rs.records.length; i < len; i++) {
6938                     var r = rs.records[i];
6939                     errors[i] = r.data;
6940                 }
6941             }
6942             if(errors.length < 1){
6943                 errors = null;
6944             }
6945             return {
6946                 success : rs.success,
6947                 errors : errors
6948             };
6949         }
6950         var ret = false;
6951         try {
6952             ret = Roo.decode(response.responseText);
6953         } catch (e) {
6954             ret = {
6955                 success: false,
6956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6957                 errors : []
6958             };
6959         }
6960         return ret;
6961         
6962     }
6963 });
6964
6965
6966 Roo.form.Action.Load = function(form, options){
6967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968     this.reader = this.form.reader;
6969 };
6970
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6972     type : 'load',
6973
6974     run : function(){
6975         
6976         Roo.Ajax.request(Roo.apply(
6977                 this.createCallback(), {
6978                     method:this.getMethod(),
6979                     url:this.getUrl(false),
6980                     params:this.getParams()
6981         }));
6982     },
6983
6984     success : function(response){
6985         
6986         var result = this.processResponse(response);
6987         if(result === true || !result.success || !result.data){
6988             this.failureType = Roo.form.Action.LOAD_FAILURE;
6989             this.form.afterAction(this, false);
6990             return;
6991         }
6992         this.form.clearInvalid();
6993         this.form.setValues(result.data);
6994         this.form.afterAction(this, true);
6995     },
6996
6997     handleResponse : function(response){
6998         if(this.form.reader){
6999             var rs = this.form.reader.read(response);
7000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001             return {
7002                 success : rs.success,
7003                 data : data
7004             };
7005         }
7006         return Roo.decode(response.responseText);
7007     }
7008 });
7009
7010 Roo.form.Action.ACTION_TYPES = {
7011     'load' : Roo.form.Action.Load,
7012     'submit' : Roo.form.Action.Submit
7013 };/*
7014  * - LGPL
7015  *
7016  * form
7017  * 
7018  */
7019
7020 /**
7021  * @class Roo.bootstrap.Form
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Form class
7024  * @cfg {String} method  GET | POST (default POST)
7025  * @cfg {String} labelAlign top | left (default top)
7026  * @cfg {String} align left  | right - for navbars
7027  * @cfg {Boolean} loadMask load mask when submit (default true)
7028
7029  * 
7030  * @constructor
7031  * Create a new Form
7032  * @param {Object} config The config object
7033  */
7034
7035
7036 Roo.bootstrap.Form = function(config){
7037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         /**
7040          * @event clientvalidation
7041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042          * @param {Form} this
7043          * @param {Boolean} valid true if the form has passed client-side validation
7044          */
7045         clientvalidation: true,
7046         /**
7047          * @event beforeaction
7048          * Fires before any action is performed. Return false to cancel the action.
7049          * @param {Form} this
7050          * @param {Action} action The action to be performed
7051          */
7052         beforeaction: true,
7053         /**
7054          * @event actionfailed
7055          * Fires when an action fails.
7056          * @param {Form} this
7057          * @param {Action} action The action that failed
7058          */
7059         actionfailed : true,
7060         /**
7061          * @event actioncomplete
7062          * Fires when an action is completed.
7063          * @param {Form} this
7064          * @param {Action} action The action that completed
7065          */
7066         actioncomplete : true
7067     });
7068     
7069 };
7070
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7072       
7073      /**
7074      * @cfg {String} method
7075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7076      */
7077     method : 'POST',
7078     /**
7079      * @cfg {String} url
7080      * The URL to use for form actions if one isn't supplied in the action options.
7081      */
7082     /**
7083      * @cfg {Boolean} fileUpload
7084      * Set to true if this form is a file upload.
7085      */
7086      
7087     /**
7088      * @cfg {Object} baseParams
7089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7090      */
7091       
7092     /**
7093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7094      */
7095     timeout: 30,
7096     /**
7097      * @cfg {Sting} align (left|right) for navbar forms
7098      */
7099     align : 'left',
7100
7101     // private
7102     activeAction : null,
7103  
7104     /**
7105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106      * element by passing it or its id or mask the form itself by passing in true.
7107      * @type Mixed
7108      */
7109     waitMsgTarget : false,
7110     
7111     loadMask : true,
7112     
7113     getAutoCreate : function(){
7114         
7115         var cfg = {
7116             tag: 'form',
7117             method : this.method || 'POST',
7118             id : this.id || Roo.id(),
7119             cls : ''
7120         };
7121         if (this.parent().xtype.match(/^Nav/)) {
7122             cfg.cls = 'navbar-form navbar-' + this.align;
7123             
7124         }
7125         
7126         if (this.labelAlign == 'left' ) {
7127             cfg.cls += ' form-horizontal';
7128         }
7129         
7130         
7131         return cfg;
7132     },
7133     initEvents : function()
7134     {
7135         this.el.on('submit', this.onSubmit, this);
7136         // this was added as random key presses on the form where triggering form submit.
7137         this.el.on('keypress', function(e) {
7138             if (e.getCharCode() != 13) {
7139                 return true;
7140             }
7141             // we might need to allow it for textareas.. and some other items.
7142             // check e.getTarget().
7143             
7144             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7145                 return true;
7146             }
7147         
7148             Roo.log("keypress blocked");
7149             
7150             e.preventDefault();
7151             return false;
7152         });
7153         
7154     },
7155     // private
7156     onSubmit : function(e){
7157         e.stopEvent();
7158     },
7159     
7160      /**
7161      * Returns true if client-side validation on the form is successful.
7162      * @return Boolean
7163      */
7164     isValid : function(){
7165         var items = this.getItems();
7166         var valid = true;
7167         items.each(function(f){
7168            if(!f.validate()){
7169                valid = false;
7170                
7171            }
7172         });
7173         return valid;
7174     },
7175     /**
7176      * Returns true if any fields in this form have changed since their original load.
7177      * @return Boolean
7178      */
7179     isDirty : function(){
7180         var dirty = false;
7181         var items = this.getItems();
7182         items.each(function(f){
7183            if(f.isDirty()){
7184                dirty = true;
7185                return false;
7186            }
7187            return true;
7188         });
7189         return dirty;
7190     },
7191      /**
7192      * Performs a predefined action (submit or load) or custom actions you define on this form.
7193      * @param {String} actionName The name of the action type
7194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196      * accept other config options):
7197      * <pre>
7198 Property          Type             Description
7199 ----------------  ---------------  ----------------------------------------------------------------------------------
7200 url               String           The url for the action (defaults to the form's url)
7201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7204                                    validate the form on the client (defaults to false)
7205      * </pre>
7206      * @return {BasicForm} this
7207      */
7208     doAction : function(action, options){
7209         if(typeof action == 'string'){
7210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211         }
7212         if(this.fireEvent('beforeaction', this, action) !== false){
7213             this.beforeAction(action);
7214             action.run.defer(100, action);
7215         }
7216         return this;
7217     },
7218     
7219     // private
7220     beforeAction : function(action){
7221         var o = action.options;
7222         
7223         if(this.loadMask){
7224             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225         }
7226         // not really supported yet.. ??
7227         
7228         //if(this.waitMsgTarget === true){
7229         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230         //}else if(this.waitMsgTarget){
7231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233         //}else {
7234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7235        // }
7236          
7237     },
7238
7239     // private
7240     afterAction : function(action, success){
7241         this.activeAction = null;
7242         var o = action.options;
7243         
7244         //if(this.waitMsgTarget === true){
7245             this.el.unmask();
7246         //}else if(this.waitMsgTarget){
7247         //    this.waitMsgTarget.unmask();
7248         //}else{
7249         //    Roo.MessageBox.updateProgress(1);
7250         //    Roo.MessageBox.hide();
7251        // }
7252         // 
7253         if(success){
7254             if(o.reset){
7255                 this.reset();
7256             }
7257             Roo.callback(o.success, o.scope, [this, action]);
7258             this.fireEvent('actioncomplete', this, action);
7259             
7260         }else{
7261             
7262             // failure condition..
7263             // we have a scenario where updates need confirming.
7264             // eg. if a locking scenario exists..
7265             // we look for { errors : { needs_confirm : true }} in the response.
7266             if (
7267                 (typeof(action.result) != 'undefined')  &&
7268                 (typeof(action.result.errors) != 'undefined')  &&
7269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7270            ){
7271                 var _t = this;
7272                 Roo.log("not supported yet");
7273                  /*
7274                 
7275                 Roo.MessageBox.confirm(
7276                     "Change requires confirmation",
7277                     action.result.errorMsg,
7278                     function(r) {
7279                         if (r != 'yes') {
7280                             return;
7281                         }
7282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7283                     }
7284                     
7285                 );
7286                 */
7287                 
7288                 
7289                 return;
7290             }
7291             
7292             Roo.callback(o.failure, o.scope, [this, action]);
7293             // show an error message if no failed handler is set..
7294             if (!this.hasListener('actionfailed')) {
7295                 Roo.log("need to add dialog support");
7296                 /*
7297                 Roo.MessageBox.alert("Error",
7298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299                         action.result.errorMsg :
7300                         "Saving Failed, please check your entries or try again"
7301                 );
7302                 */
7303             }
7304             
7305             this.fireEvent('actionfailed', this, action);
7306         }
7307         
7308     },
7309     /**
7310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311      * @param {String} id The value to search for
7312      * @return Field
7313      */
7314     findField : function(id){
7315         var items = this.getItems();
7316         var field = items.get(id);
7317         if(!field){
7318              items.each(function(f){
7319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7320                     field = f;
7321                     return false;
7322                 }
7323                 return true;
7324             });
7325         }
7326         return field || null;
7327     },
7328      /**
7329      * Mark fields in this form invalid in bulk.
7330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331      * @return {BasicForm} this
7332      */
7333     markInvalid : function(errors){
7334         if(errors instanceof Array){
7335             for(var i = 0, len = errors.length; i < len; i++){
7336                 var fieldError = errors[i];
7337                 var f = this.findField(fieldError.id);
7338                 if(f){
7339                     f.markInvalid(fieldError.msg);
7340                 }
7341             }
7342         }else{
7343             var field, id;
7344             for(id in errors){
7345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346                     field.markInvalid(errors[id]);
7347                 }
7348             }
7349         }
7350         //Roo.each(this.childForms || [], function (f) {
7351         //    f.markInvalid(errors);
7352         //});
7353         
7354         return this;
7355     },
7356
7357     /**
7358      * Set values for fields in this form in bulk.
7359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360      * @return {BasicForm} this
7361      */
7362     setValues : function(values){
7363         if(values instanceof Array){ // array of objects
7364             for(var i = 0, len = values.length; i < len; i++){
7365                 var v = values[i];
7366                 var f = this.findField(v.id);
7367                 if(f){
7368                     f.setValue(v.value);
7369                     if(this.trackResetOnLoad){
7370                         f.originalValue = f.getValue();
7371                     }
7372                 }
7373             }
7374         }else{ // object hash
7375             var field, id;
7376             for(id in values){
7377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378                     
7379                     if (field.setFromData && 
7380                         field.valueField && 
7381                         field.displayField &&
7382                         // combos' with local stores can 
7383                         // be queried via setValue()
7384                         // to set their value..
7385                         (field.store && !field.store.isLocal)
7386                         ) {
7387                         // it's a combo
7388                         var sd = { };
7389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391                         field.setFromData(sd);
7392                         
7393                     } else {
7394                         field.setValue(values[id]);
7395                     }
7396                     
7397                     
7398                     if(this.trackResetOnLoad){
7399                         field.originalValue = field.getValue();
7400                     }
7401                 }
7402             }
7403         }
7404          
7405         //Roo.each(this.childForms || [], function (f) {
7406         //    f.setValues(values);
7407         //});
7408                 
7409         return this;
7410     },
7411
7412     /**
7413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414      * they are returned as an array.
7415      * @param {Boolean} asString
7416      * @return {Object}
7417      */
7418     getValues : function(asString){
7419         //if (this.childForms) {
7420             // copy values from the child forms
7421         //    Roo.each(this.childForms, function (f) {
7422         //        this.setValues(f.getValues());
7423         //    }, this);
7424         //}
7425         
7426         
7427         
7428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429         if(asString === true){
7430             return fs;
7431         }
7432         return Roo.urlDecode(fs);
7433     },
7434     
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. 
7437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7438      * @return {Object}
7439      */
7440     getFieldValues : function(with_hidden)
7441     {
7442         var items = this.getItems();
7443         var ret = {};
7444         items.each(function(f){
7445             if (!f.getName()) {
7446                 return;
7447             }
7448             var v = f.getValue();
7449             if (f.inputType =='radio') {
7450                 if (typeof(ret[f.getName()]) == 'undefined') {
7451                     ret[f.getName()] = ''; // empty..
7452                 }
7453                 
7454                 if (!f.el.dom.checked) {
7455                     return;
7456                     
7457                 }
7458                 v = f.el.dom.value;
7459                 
7460             }
7461             
7462             // not sure if this supported any more..
7463             if ((typeof(v) == 'object') && f.getRawValue) {
7464                 v = f.getRawValue() ; // dates..
7465             }
7466             // combo boxes where name != hiddenName...
7467             if (f.name != f.getName()) {
7468                 ret[f.name] = f.getRawValue();
7469             }
7470             ret[f.getName()] = v;
7471         });
7472         
7473         return ret;
7474     },
7475
7476     /**
7477      * Clears all invalid messages in this form.
7478      * @return {BasicForm} this
7479      */
7480     clearInvalid : function(){
7481         var items = this.getItems();
7482         
7483         items.each(function(f){
7484            f.clearInvalid();
7485         });
7486         
7487         
7488         
7489         return this;
7490     },
7491
7492     /**
7493      * Resets this form.
7494      * @return {BasicForm} this
7495      */
7496     reset : function(){
7497         var items = this.getItems();
7498         items.each(function(f){
7499             f.reset();
7500         });
7501         
7502         Roo.each(this.childForms || [], function (f) {
7503             f.reset();
7504         });
7505        
7506         
7507         return this;
7508     },
7509     getItems : function()
7510     {
7511         var r=new Roo.util.MixedCollection(false, function(o){
7512             return o.id || (o.id = Roo.id());
7513         });
7514         var iter = function(el) {
7515             if (el.inputEl) {
7516                 r.add(el);
7517             }
7518             if (!el.items) {
7519                 return;
7520             }
7521             Roo.each(el.items,function(e) {
7522                 iter(e);
7523             });
7524             
7525             
7526         };
7527         
7528         iter(this);
7529         return r;
7530         
7531         
7532         
7533         
7534     }
7535     
7536 });
7537
7538  
7539 /*
7540  * Based on:
7541  * Ext JS Library 1.1.1
7542  * Copyright(c) 2006-2007, Ext JS, LLC.
7543  *
7544  * Originally Released Under LGPL - original licence link has changed is not relivant.
7545  *
7546  * Fork - LGPL
7547  * <script type="text/javascript">
7548  */
7549 /**
7550  * @class Roo.form.VTypes
7551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7552  * @singleton
7553  */
7554 Roo.form.VTypes = function(){
7555     // closure these in so they are only created once.
7556     var alpha = /^[a-zA-Z_]+$/;
7557     var alphanum = /^[a-zA-Z0-9_]+$/;
7558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560
7561     // All these messages and functions are configurable
7562     return {
7563         /**
7564          * The function used to validate email addresses
7565          * @param {String} value The email address
7566          */
7567         'email' : function(v){
7568             return email.test(v);
7569         },
7570         /**
7571          * The error text to display when the email validation function returns false
7572          * @type String
7573          */
7574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575         /**
7576          * The keystroke filter mask to be applied on email input
7577          * @type RegExp
7578          */
7579         'emailMask' : /[a-z0-9_\.\-@]/i,
7580
7581         /**
7582          * The function used to validate URLs
7583          * @param {String} value The URL
7584          */
7585         'url' : function(v){
7586             return url.test(v);
7587         },
7588         /**
7589          * The error text to display when the url validation function returns false
7590          * @type String
7591          */
7592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7593         
7594         /**
7595          * The function used to validate alpha values
7596          * @param {String} value The value
7597          */
7598         'alpha' : function(v){
7599             return alpha.test(v);
7600         },
7601         /**
7602          * The error text to display when the alpha validation function returns false
7603          * @type String
7604          */
7605         'alphaText' : 'This field should only contain letters and _',
7606         /**
7607          * The keystroke filter mask to be applied on alpha input
7608          * @type RegExp
7609          */
7610         'alphaMask' : /[a-z_]/i,
7611
7612         /**
7613          * The function used to validate alphanumeric values
7614          * @param {String} value The value
7615          */
7616         'alphanum' : function(v){
7617             return alphanum.test(v);
7618         },
7619         /**
7620          * The error text to display when the alphanumeric validation function returns false
7621          * @type String
7622          */
7623         'alphanumText' : 'This field should only contain letters, numbers and _',
7624         /**
7625          * The keystroke filter mask to be applied on alphanumeric input
7626          * @type RegExp
7627          */
7628         'alphanumMask' : /[a-z0-9_]/i
7629     };
7630 }();/*
7631  * - LGPL
7632  *
7633  * Input
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Input
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Input class
7641  * @cfg {Boolean} disabled is it disabled
7642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643  * @cfg {String} name name of the input
7644  * @cfg {string} fieldLabel - the label associated
7645  * @cfg {string} placeholder - placeholder to put in text.
7646  * @cfg {string}  before - input group add on before
7647  * @cfg {string} after - input group add on after
7648  * @cfg {string} size - (lg|sm) or leave empty..
7649  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651  * @cfg {Number} md colspan out of 12 for computer-sized screens
7652  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653  * @cfg {string} value default value of the input
7654  * @cfg {Number} labelWidth set the width of label (0-12)
7655  * @cfg {String} labelAlign (top|left)
7656  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7658
7659  * @cfg {String} align (left|center|right) Default left
7660  * @cfg {Boolean} forceFeedback (true|false) Default false
7661  * 
7662  * 
7663  * 
7664  * 
7665  * @constructor
7666  * Create a new Input
7667  * @param {Object} config The config object
7668  */
7669
7670 Roo.bootstrap.Input = function(config){
7671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7672    
7673         this.addEvents({
7674             /**
7675              * @event focus
7676              * Fires when this field receives input focus.
7677              * @param {Roo.form.Field} this
7678              */
7679             focus : true,
7680             /**
7681              * @event blur
7682              * Fires when this field loses input focus.
7683              * @param {Roo.form.Field} this
7684              */
7685             blur : true,
7686             /**
7687              * @event specialkey
7688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690              * @param {Roo.form.Field} this
7691              * @param {Roo.EventObject} e The event object
7692              */
7693             specialkey : true,
7694             /**
7695              * @event change
7696              * Fires just before the field blurs if the field value has changed.
7697              * @param {Roo.form.Field} this
7698              * @param {Mixed} newValue The new value
7699              * @param {Mixed} oldValue The original value
7700              */
7701             change : true,
7702             /**
7703              * @event invalid
7704              * Fires after the field has been marked as invalid.
7705              * @param {Roo.form.Field} this
7706              * @param {String} msg The validation message
7707              */
7708             invalid : true,
7709             /**
7710              * @event valid
7711              * Fires after the field has been validated with no errors.
7712              * @param {Roo.form.Field} this
7713              */
7714             valid : true,
7715              /**
7716              * @event keyup
7717              * Fires after the key up
7718              * @param {Roo.form.Field} this
7719              * @param {Roo.EventObject}  e The event Object
7720              */
7721             keyup : true
7722         });
7723 };
7724
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7726      /**
7727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728       automatic validation (defaults to "keyup").
7729      */
7730     validationEvent : "keyup",
7731      /**
7732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7733      */
7734     validateOnBlur : true,
7735     /**
7736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7737      */
7738     validationDelay : 250,
7739      /**
7740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7741      */
7742     focusClass : "x-form-focus",  // not needed???
7743     
7744        
7745     /**
7746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7747      */
7748     invalidClass : "has-warning",
7749     
7750     /**
7751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7752      */
7753     validClass : "has-success",
7754     
7755     /**
7756      * @cfg {Boolean} hasFeedback (true|false) default true
7757      */
7758     hasFeedback : true,
7759     
7760     /**
7761      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7762      */
7763     invalidFeedbackClass : "glyphicon-warning-sign",
7764     
7765     /**
7766      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7767      */
7768     validFeedbackClass : "glyphicon-ok",
7769     
7770     /**
7771      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7772      */
7773     selectOnFocus : false,
7774     
7775      /**
7776      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7777      */
7778     maskRe : null,
7779        /**
7780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7781      */
7782     vtype : null,
7783     
7784       /**
7785      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7786      */
7787     disableKeyFilter : false,
7788     
7789        /**
7790      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7791      */
7792     disabled : false,
7793      /**
7794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7795      */
7796     allowBlank : true,
7797     /**
7798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7799      */
7800     blankText : "This field is required",
7801     
7802      /**
7803      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7804      */
7805     minLength : 0,
7806     /**
7807      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7808      */
7809     maxLength : Number.MAX_VALUE,
7810     /**
7811      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7812      */
7813     minLengthText : "The minimum length for this field is {0}",
7814     /**
7815      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7816      */
7817     maxLengthText : "The maximum length for this field is {0}",
7818   
7819     
7820     /**
7821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822      * If available, this function will be called only after the basic validators all return true, and will be passed the
7823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7824      */
7825     validator : null,
7826     /**
7827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7830      */
7831     regex : null,
7832     /**
7833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7834      */
7835     regexText : "",
7836     
7837     autocomplete: false,
7838     
7839     
7840     fieldLabel : '',
7841     inputType : 'text',
7842     
7843     name : false,
7844     placeholder: false,
7845     before : false,
7846     after : false,
7847     size : false,
7848     hasFocus : false,
7849     preventMark: false,
7850     isFormField : true,
7851     value : '',
7852     labelWidth : 2,
7853     labelAlign : false,
7854     readOnly : false,
7855     align : false,
7856     formatedValue : false,
7857     forceFeedback : false,
7858     
7859     parentLabelAlign : function()
7860     {
7861         var parent = this;
7862         while (parent.parent()) {
7863             parent = parent.parent();
7864             if (typeof(parent.labelAlign) !='undefined') {
7865                 return parent.labelAlign;
7866             }
7867         }
7868         return 'left';
7869         
7870     },
7871     
7872     getAutoCreate : function(){
7873         
7874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7875         
7876         var id = Roo.id();
7877         
7878         var cfg = {};
7879         
7880         if(this.inputType != 'hidden'){
7881             cfg.cls = 'form-group' //input-group
7882         }
7883         
7884         var input =  {
7885             tag: 'input',
7886             id : id,
7887             type : this.inputType,
7888             value : this.value,
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '',
7891             autocomplete : this.autocomplete || 'new-password'
7892         };
7893         
7894         
7895         if(this.align){
7896             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7897         }
7898         
7899         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900             input.maxLength = this.maxLength;
7901         }
7902         
7903         if (this.disabled) {
7904             input.disabled=true;
7905         }
7906         
7907         if (this.readOnly) {
7908             input.readonly=true;
7909         }
7910         
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         var inputblock = input;
7925         
7926         var feedback = {
7927             tag: 'span',
7928             cls: 'glyphicon form-control-feedback'
7929         };
7930             
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7932             
7933             inputblock = {
7934                 cls : 'has-feedback',
7935                 cn :  [
7936                     input,
7937                     feedback
7938                 ] 
7939             };  
7940         }
7941         
7942         if (this.before || this.after) {
7943             
7944             inputblock = {
7945                 cls : 'input-group',
7946                 cn :  [] 
7947             };
7948             
7949             if (this.before && typeof(this.before) == 'string') {
7950                 
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'roo-input-before input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             if (this.before && typeof(this.before) == 'object') {
7958                 this.before = Roo.factory(this.before);
7959                 Roo.log(this.before);
7960                 inputblock.cn.push({
7961                     tag :'span',
7962                     cls : 'roo-input-before input-group-' +
7963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7964                 });
7965             }
7966             
7967             inputblock.cn.push(input);
7968             
7969             if (this.after && typeof(this.after) == 'string') {
7970                 inputblock.cn.push({
7971                     tag :'span',
7972                     cls : 'roo-input-after input-group-addon',
7973                     html : this.after
7974                 });
7975             }
7976             if (this.after && typeof(this.after) == 'object') {
7977                 this.after = Roo.factory(this.after);
7978                 Roo.log(this.after);
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'roo-input-after input-group-' +
7982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7983                 });
7984             }
7985             
7986             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987                 inputblock.cls += ' has-feedback';
7988                 inputblock.cn.push(feedback);
7989             }
7990         };
7991         
7992         if (align ==='left' && this.fieldLabel.length) {
7993                 Roo.log("left and has label");
7994                 cfg.cn = [
7995                     
7996                     {
7997                         tag: 'label',
7998                         'for' :  id,
7999                         cls : 'control-label col-sm-' + this.labelWidth,
8000                         html : this.fieldLabel
8001                         
8002                     },
8003                     {
8004                         cls : "col-sm-" + (12 - this.labelWidth), 
8005                         cn: [
8006                             inputblock
8007                         ]
8008                     }
8009                     
8010                 ];
8011         } else if ( this.fieldLabel.length) {
8012                 Roo.log(" label");
8013                  cfg.cn = [
8014                    
8015                     {
8016                         tag: 'label',
8017                         //cls : 'input-group-addon',
8018                         html : this.fieldLabel
8019                         
8020                     },
8021                     
8022                     inputblock
8023                     
8024                 ];
8025
8026         } else {
8027             
8028                 Roo.log(" no label && no align");
8029                 cfg.cn = [
8030                     
8031                         inputblock
8032                     
8033                 ];
8034                 
8035                 
8036         };
8037         Roo.log('input-parentType: ' + this.parentType);
8038         
8039         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8040            cfg.cls += ' navbar-form';
8041            Roo.log(cfg);
8042         }
8043         
8044         return cfg;
8045         
8046     },
8047     /**
8048      * return the real input element.
8049      */
8050     inputEl: function ()
8051     {
8052         return this.el.select('input.form-control',true).first();
8053     },
8054     
8055     tooltipEl : function()
8056     {
8057         return this.inputEl();
8058     },
8059     
8060     setDisabled : function(v)
8061     {
8062         var i  = this.inputEl().dom;
8063         if (!v) {
8064             i.removeAttribute('disabled');
8065             return;
8066             
8067         }
8068         i.setAttribute('disabled','true');
8069     },
8070     initEvents : function()
8071     {
8072           
8073         this.inputEl().on("keydown" , this.fireKey,  this);
8074         this.inputEl().on("focus", this.onFocus,  this);
8075         this.inputEl().on("blur", this.onBlur,  this);
8076         
8077         this.inputEl().relayEvent('keyup', this);
8078  
8079         // reference to original value for reset
8080         this.originalValue = this.getValue();
8081         //Roo.form.TextField.superclass.initEvents.call(this);
8082         if(this.validationEvent == 'keyup'){
8083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084             this.inputEl().on('keyup', this.filterValidation, this);
8085         }
8086         else if(this.validationEvent !== false){
8087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8088         }
8089         
8090         if(this.selectOnFocus){
8091             this.on("focus", this.preFocus, this);
8092             
8093         }
8094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095             this.inputEl().on("keypress", this.filterKeys, this);
8096         }
8097        /* if(this.grow){
8098             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8099             this.el.on("click", this.autoSize,  this);
8100         }
8101         */
8102         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8104         }
8105         
8106         if (typeof(this.before) == 'object') {
8107             this.before.render(this.el.select('.roo-input-before',true).first());
8108         }
8109         if (typeof(this.after) == 'object') {
8110             this.after.render(this.el.select('.roo-input-after',true).first());
8111         }
8112         
8113         
8114     },
8115     filterValidation : function(e){
8116         if(!e.isNavKeyPress()){
8117             this.validationTask.delay(this.validationDelay);
8118         }
8119     },
8120      /**
8121      * Validates the field value
8122      * @return {Boolean} True if the value is valid, else false
8123      */
8124     validate : function(){
8125         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126         if(this.disabled || this.validateValue(this.getRawValue())){
8127             this.markValid();
8128             return true;
8129         }
8130         
8131         this.markInvalid();
8132         return false;
8133     },
8134     
8135     
8136     /**
8137      * Validates a value according to the field's validation rules and marks the field as invalid
8138      * if the validation fails
8139      * @param {Mixed} value The value to validate
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validateValue : function(value){
8143         if(value.length < 1)  { // if it's blank
8144             if(this.allowBlank){
8145                 return true;
8146             }
8147             return false;
8148         }
8149         
8150         if(value.length < this.minLength){
8151             return false;
8152         }
8153         if(value.length > this.maxLength){
8154             return false;
8155         }
8156         if(this.vtype){
8157             var vt = Roo.form.VTypes;
8158             if(!vt[this.vtype](value, this)){
8159                 return false;
8160             }
8161         }
8162         if(typeof this.validator == "function"){
8163             var msg = this.validator(value);
8164             if(msg !== true){
8165                 return false;
8166             }
8167         }
8168         
8169         if(this.regex && !this.regex.test(value)){
8170             return false;
8171         }
8172         
8173         return true;
8174     },
8175
8176     
8177     
8178      // private
8179     fireKey : function(e){
8180         //Roo.log('field ' + e.getKey());
8181         if(e.isNavKeyPress()){
8182             this.fireEvent("specialkey", this, e);
8183         }
8184     },
8185     focus : function (selectText){
8186         if(this.rendered){
8187             this.inputEl().focus();
8188             if(selectText === true){
8189                 this.inputEl().dom.select();
8190             }
8191         }
8192         return this;
8193     } ,
8194     
8195     onFocus : function(){
8196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197            // this.el.addClass(this.focusClass);
8198         }
8199         if(!this.hasFocus){
8200             this.hasFocus = true;
8201             this.startValue = this.getValue();
8202             this.fireEvent("focus", this);
8203         }
8204     },
8205     
8206     beforeBlur : Roo.emptyFn,
8207
8208     
8209     // private
8210     onBlur : function(){
8211         this.beforeBlur();
8212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213             //this.el.removeClass(this.focusClass);
8214         }
8215         this.hasFocus = false;
8216         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8217             this.validate();
8218         }
8219         var v = this.getValue();
8220         if(String(v) !== String(this.startValue)){
8221             this.fireEvent('change', this, v, this.startValue);
8222         }
8223         this.fireEvent("blur", this);
8224     },
8225     
8226     /**
8227      * Resets the current field value to the originally loaded value and clears any validation messages
8228      */
8229     reset : function(){
8230         this.setValue(this.originalValue);
8231         this.validate();
8232     },
8233      /**
8234      * Returns the name of the field
8235      * @return {Mixed} name The name field
8236      */
8237     getName: function(){
8238         return this.name;
8239     },
8240      /**
8241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8242      * @return {Mixed} value The field value
8243      */
8244     getValue : function(){
8245         
8246         var v = this.inputEl().getValue();
8247         
8248         return v;
8249     },
8250     /**
8251      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8252      * @return {Mixed} value The field value
8253      */
8254     getRawValue : function(){
8255         var v = this.inputEl().getValue();
8256         
8257         return v;
8258     },
8259     
8260     /**
8261      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8262      * @param {Mixed} value The value to set
8263      */
8264     setRawValue : function(v){
8265         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8266     },
8267     
8268     selectText : function(start, end){
8269         var v = this.getRawValue();
8270         if(v.length > 0){
8271             start = start === undefined ? 0 : start;
8272             end = end === undefined ? v.length : end;
8273             var d = this.inputEl().dom;
8274             if(d.setSelectionRange){
8275                 d.setSelectionRange(start, end);
8276             }else if(d.createTextRange){
8277                 var range = d.createTextRange();
8278                 range.moveStart("character", start);
8279                 range.moveEnd("character", v.length-end);
8280                 range.select();
8281             }
8282         }
8283     },
8284     
8285     /**
8286      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8287      * @param {Mixed} value The value to set
8288      */
8289     setValue : function(v){
8290         this.value = v;
8291         if(this.rendered){
8292             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8293             this.validate();
8294         }
8295     },
8296     
8297     /*
8298     processValue : function(value){
8299         if(this.stripCharsRe){
8300             var newValue = value.replace(this.stripCharsRe, '');
8301             if(newValue !== value){
8302                 this.setRawValue(newValue);
8303                 return newValue;
8304             }
8305         }
8306         return value;
8307     },
8308   */
8309     preFocus : function(){
8310         
8311         if(this.selectOnFocus){
8312             this.inputEl().dom.select();
8313         }
8314     },
8315     filterKeys : function(e){
8316         var k = e.getKey();
8317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8318             return;
8319         }
8320         var c = e.getCharCode(), cc = String.fromCharCode(c);
8321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8322             return;
8323         }
8324         if(!this.maskRe.test(cc)){
8325             e.stopEvent();
8326         }
8327     },
8328      /**
8329      * Clear any invalid styles/messages for this field
8330      */
8331     clearInvalid : function(){
8332         
8333         if(!this.el || this.preventMark){ // not rendered
8334             return;
8335         }
8336         this.el.removeClass(this.invalidClass);
8337         
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             var feedback = this.el.select('.form-control-feedback', true).first();
8341             
8342             if(feedback){
8343                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8344             }
8345             
8346         }
8347         
8348         this.fireEvent('valid', this);
8349     },
8350     
8351      /**
8352      * Mark this field as valid
8353      */
8354     markValid : function(){
8355         if(!this.el  || this.preventMark){ // not rendered
8356             return;
8357         }
8358         
8359         this.el.removeClass([this.invalidClass, this.validClass]);
8360         
8361         var feedback = this.el.select('.form-control-feedback', true).first();
8362             
8363         if(feedback){
8364             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8365         }
8366
8367         if(this.disabled || this.allowBlank){
8368             return;
8369         }
8370         
8371         this.el.addClass(this.validClass);
8372         
8373         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8374             
8375             var feedback = this.el.select('.form-control-feedback', true).first();
8376             
8377             if(feedback){
8378                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8379                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8380             }
8381             
8382         }
8383         
8384         this.fireEvent('valid', this);
8385     },
8386     
8387      /**
8388      * Mark this field as invalid
8389      * @param {String} msg The validation message
8390      */
8391     markInvalid : function(msg)
8392     {
8393         if(!this.el  || this.preventMark){ // not rendered
8394             return;
8395         }
8396         
8397         this.el.removeClass([this.invalidClass, this.validClass]);
8398         
8399         var feedback = this.el.select('.form-control-feedback', true).first();
8400             
8401         if(feedback){
8402             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8403         }
8404
8405         if(this.disabled || this.allowBlank){
8406             return;
8407         }
8408         
8409         this.el.addClass(this.invalidClass);
8410         
8411         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8412             
8413             var feedback = this.el.select('.form-control-feedback', true).first();
8414             
8415             if(feedback){
8416                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8417                 
8418                 if(this.getValue().length || this.forceFeedback){
8419                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8420                 }
8421                 
8422             }
8423             
8424         }
8425         
8426         this.fireEvent('invalid', this, msg);
8427     },
8428     // private
8429     SafariOnKeyDown : function(event)
8430     {
8431         // this is a workaround for a password hang bug on chrome/ webkit.
8432         
8433         var isSelectAll = false;
8434         
8435         if(this.inputEl().dom.selectionEnd > 0){
8436             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8437         }
8438         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8439             event.preventDefault();
8440             this.setValue('');
8441             return;
8442         }
8443         
8444         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8445             
8446             event.preventDefault();
8447             // this is very hacky as keydown always get's upper case.
8448             //
8449             var cc = String.fromCharCode(event.getCharCode());
8450             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8451             
8452         }
8453     },
8454     adjustWidth : function(tag, w){
8455         tag = tag.toLowerCase();
8456         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8457             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8458                 if(tag == 'input'){
8459                     return w + 2;
8460                 }
8461                 if(tag == 'textarea'){
8462                     return w-2;
8463                 }
8464             }else if(Roo.isOpera){
8465                 if(tag == 'input'){
8466                     return w + 2;
8467                 }
8468                 if(tag == 'textarea'){
8469                     return w-2;
8470                 }
8471             }
8472         }
8473         return w;
8474     }
8475     
8476 });
8477
8478  
8479 /*
8480  * - LGPL
8481  *
8482  * Input
8483  * 
8484  */
8485
8486 /**
8487  * @class Roo.bootstrap.TextArea
8488  * @extends Roo.bootstrap.Input
8489  * Bootstrap TextArea class
8490  * @cfg {Number} cols Specifies the visible width of a text area
8491  * @cfg {Number} rows Specifies the visible number of lines in a text area
8492  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8493  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8494  * @cfg {string} html text
8495  * 
8496  * @constructor
8497  * Create a new TextArea
8498  * @param {Object} config The config object
8499  */
8500
8501 Roo.bootstrap.TextArea = function(config){
8502     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8503    
8504 };
8505
8506 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8507      
8508     cols : false,
8509     rows : 5,
8510     readOnly : false,
8511     warp : 'soft',
8512     resize : false,
8513     value: false,
8514     html: false,
8515     
8516     getAutoCreate : function(){
8517         
8518         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8519         
8520         var id = Roo.id();
8521         
8522         var cfg = {};
8523         
8524         var input =  {
8525             tag: 'textarea',
8526             id : id,
8527             warp : this.warp,
8528             rows : this.rows,
8529             value : this.value || '',
8530             html: this.html || '',
8531             cls : 'form-control',
8532             placeholder : this.placeholder || '' 
8533             
8534         };
8535         
8536         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8537             input.maxLength = this.maxLength;
8538         }
8539         
8540         if(this.resize){
8541             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8542         }
8543         
8544         if(this.cols){
8545             input.cols = this.cols;
8546         }
8547         
8548         if (this.readOnly) {
8549             input.readonly = true;
8550         }
8551         
8552         if (this.name) {
8553             input.name = this.name;
8554         }
8555         
8556         if (this.size) {
8557             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8558         }
8559         
8560         var settings=this;
8561         ['xs','sm','md','lg'].map(function(size){
8562             if (settings[size]) {
8563                 cfg.cls += ' col-' + size + '-' + settings[size];
8564             }
8565         });
8566         
8567         var inputblock = input;
8568         
8569         if(this.hasFeedback && !this.allowBlank){
8570             
8571             var feedback = {
8572                 tag: 'span',
8573                 cls: 'glyphicon form-control-feedback'
8574             };
8575
8576             inputblock = {
8577                 cls : 'has-feedback',
8578                 cn :  [
8579                     input,
8580                     feedback
8581                 ] 
8582             };  
8583         }
8584         
8585         
8586         if (this.before || this.after) {
8587             
8588             inputblock = {
8589                 cls : 'input-group',
8590                 cn :  [] 
8591             };
8592             if (this.before) {
8593                 inputblock.cn.push({
8594                     tag :'span',
8595                     cls : 'input-group-addon',
8596                     html : this.before
8597                 });
8598             }
8599             
8600             inputblock.cn.push(input);
8601             
8602             if(this.hasFeedback && !this.allowBlank){
8603                 inputblock.cls += ' has-feedback';
8604                 inputblock.cn.push(feedback);
8605             }
8606             
8607             if (this.after) {
8608                 inputblock.cn.push({
8609                     tag :'span',
8610                     cls : 'input-group-addon',
8611                     html : this.after
8612                 });
8613             }
8614             
8615         }
8616         
8617         if (align ==='left' && this.fieldLabel.length) {
8618                 Roo.log("left and has label");
8619                 cfg.cn = [
8620                     
8621                     {
8622                         tag: 'label',
8623                         'for' :  id,
8624                         cls : 'control-label col-sm-' + this.labelWidth,
8625                         html : this.fieldLabel
8626                         
8627                     },
8628                     {
8629                         cls : "col-sm-" + (12 - this.labelWidth), 
8630                         cn: [
8631                             inputblock
8632                         ]
8633                     }
8634                     
8635                 ];
8636         } else if ( this.fieldLabel.length) {
8637                 Roo.log(" label");
8638                  cfg.cn = [
8639                    
8640                     {
8641                         tag: 'label',
8642                         //cls : 'input-group-addon',
8643                         html : this.fieldLabel
8644                         
8645                     },
8646                     
8647                     inputblock
8648                     
8649                 ];
8650
8651         } else {
8652             
8653                    Roo.log(" no label && no align");
8654                 cfg.cn = [
8655                     
8656                         inputblock
8657                     
8658                 ];
8659                 
8660                 
8661         }
8662         
8663         if (this.disabled) {
8664             input.disabled=true;
8665         }
8666         
8667         return cfg;
8668         
8669     },
8670     /**
8671      * return the real textarea element.
8672      */
8673     inputEl: function ()
8674     {
8675         return this.el.select('textarea.form-control',true).first();
8676     }
8677 });
8678
8679  
8680 /*
8681  * - LGPL
8682  *
8683  * trigger field - base class for combo..
8684  * 
8685  */
8686  
8687 /**
8688  * @class Roo.bootstrap.TriggerField
8689  * @extends Roo.bootstrap.Input
8690  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8691  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8692  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8693  * for which you can provide a custom implementation.  For example:
8694  * <pre><code>
8695 var trigger = new Roo.bootstrap.TriggerField();
8696 trigger.onTriggerClick = myTriggerFn;
8697 trigger.applyTo('my-field');
8698 </code></pre>
8699  *
8700  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8701  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8702  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8703  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8704  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8705
8706  * @constructor
8707  * Create a new TriggerField.
8708  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8709  * to the base TextField)
8710  */
8711 Roo.bootstrap.TriggerField = function(config){
8712     this.mimicing = false;
8713     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8714 };
8715
8716 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8717     /**
8718      * @cfg {String} triggerClass A CSS class to apply to the trigger
8719      */
8720      /**
8721      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8722      */
8723     hideTrigger:false,
8724
8725     /**
8726      * @cfg {Boolean} removable (true|false) special filter default false
8727      */
8728     removable : false,
8729     
8730     /** @cfg {Boolean} grow @hide */
8731     /** @cfg {Number} growMin @hide */
8732     /** @cfg {Number} growMax @hide */
8733
8734     /**
8735      * @hide 
8736      * @method
8737      */
8738     autoSize: Roo.emptyFn,
8739     // private
8740     monitorTab : true,
8741     // private
8742     deferHeight : true,
8743
8744     
8745     actionMode : 'wrap',
8746     
8747     caret : false,
8748     
8749     
8750     getAutoCreate : function(){
8751        
8752         var align = this.labelAlign || this.parentLabelAlign();
8753         
8754         var id = Roo.id();
8755         
8756         var cfg = {
8757             cls: 'form-group' //input-group
8758         };
8759         
8760         
8761         var input =  {
8762             tag: 'input',
8763             id : id,
8764             type : this.inputType,
8765             cls : 'form-control',
8766             autocomplete: 'new-password',
8767             placeholder : this.placeholder || '' 
8768             
8769         };
8770         if (this.name) {
8771             input.name = this.name;
8772         }
8773         if (this.size) {
8774             input.cls += ' input-' + this.size;
8775         }
8776         
8777         if (this.disabled) {
8778             input.disabled=true;
8779         }
8780         
8781         var inputblock = input;
8782         
8783         if(this.hasFeedback && !this.allowBlank){
8784             
8785             var feedback = {
8786                 tag: 'span',
8787                 cls: 'glyphicon form-control-feedback'
8788             };
8789             
8790             if(this.removable && !this.editable && !this.tickable){
8791                 inputblock = {
8792                     cls : 'has-feedback',
8793                     cn :  [
8794                         inputblock,
8795                         {
8796                             tag: 'button',
8797                             html : 'x',
8798                             cls : 'roo-combo-removable-btn close'
8799                         },
8800                         feedback
8801                     ] 
8802                 };
8803             } else {
8804                 inputblock = {
8805                     cls : 'has-feedback',
8806                     cn :  [
8807                         inputblock,
8808                         feedback
8809                     ] 
8810                 };
8811             }
8812
8813         } else {
8814             if(this.removable && !this.editable && !this.tickable){
8815                 inputblock = {
8816                     cls : 'roo-removable',
8817                     cn :  [
8818                         inputblock,
8819                         {
8820                             tag: 'button',
8821                             html : 'x',
8822                             cls : 'roo-combo-removable-btn close'
8823                         }
8824                     ] 
8825                 };
8826             }
8827         }
8828         
8829         if (this.before || this.after) {
8830             
8831             inputblock = {
8832                 cls : 'input-group',
8833                 cn :  [] 
8834             };
8835             if (this.before) {
8836                 inputblock.cn.push({
8837                     tag :'span',
8838                     cls : 'input-group-addon',
8839                     html : this.before
8840                 });
8841             }
8842             
8843             inputblock.cn.push(input);
8844             
8845             if(this.hasFeedback && !this.allowBlank){
8846                 inputblock.cls += ' has-feedback';
8847                 inputblock.cn.push(feedback);
8848             }
8849             
8850             if (this.after) {
8851                 inputblock.cn.push({
8852                     tag :'span',
8853                     cls : 'input-group-addon',
8854                     html : this.after
8855                 });
8856             }
8857             
8858         };
8859         
8860         var box = {
8861             tag: 'div',
8862             cn: [
8863                 {
8864                     tag: 'input',
8865                     type : 'hidden',
8866                     cls: 'form-hidden-field'
8867                 },
8868                 inputblock
8869             ]
8870             
8871         };
8872         
8873         if(this.multiple){
8874             Roo.log('multiple');
8875             
8876             box = {
8877                 tag: 'div',
8878                 cn: [
8879                     {
8880                         tag: 'input',
8881                         type : 'hidden',
8882                         cls: 'form-hidden-field'
8883                     },
8884                     {
8885                         tag: 'ul',
8886                         cls: 'select2-choices',
8887                         cn:[
8888                             {
8889                                 tag: 'li',
8890                                 cls: 'select2-search-field',
8891                                 cn: [
8892
8893                                     inputblock
8894                                 ]
8895                             }
8896                         ]
8897                     }
8898                 ]
8899             }
8900         };
8901         
8902         var combobox = {
8903             cls: 'select2-container input-group',
8904             cn: [
8905                 box
8906 //                {
8907 //                    tag: 'ul',
8908 //                    cls: 'typeahead typeahead-long dropdown-menu',
8909 //                    style: 'display:none'
8910 //                }
8911             ]
8912         };
8913         
8914         if(!this.multiple && this.showToggleBtn){
8915             
8916             var caret = {
8917                         tag: 'span',
8918                         cls: 'caret'
8919              };
8920             if (this.caret != false) {
8921                 caret = {
8922                      tag: 'i',
8923                      cls: 'fa fa-' + this.caret
8924                 };
8925                 
8926             }
8927             
8928             combobox.cn.push({
8929                 tag :'span',
8930                 cls : 'input-group-addon btn dropdown-toggle',
8931                 cn : [
8932                     caret,
8933                     {
8934                         tag: 'span',
8935                         cls: 'combobox-clear',
8936                         cn  : [
8937                             {
8938                                 tag : 'i',
8939                                 cls: 'icon-remove'
8940                             }
8941                         ]
8942                     }
8943                 ]
8944
8945             })
8946         }
8947         
8948         if(this.multiple){
8949             combobox.cls += ' select2-container-multi';
8950         }
8951         
8952         if (align ==='left' && this.fieldLabel.length) {
8953             
8954                 Roo.log("left and has label");
8955                 cfg.cn = [
8956                     
8957                     {
8958                         tag: 'label',
8959                         'for' :  id,
8960                         cls : 'control-label col-sm-' + this.labelWidth,
8961                         html : this.fieldLabel
8962                         
8963                     },
8964                     {
8965                         cls : "col-sm-" + (12 - this.labelWidth), 
8966                         cn: [
8967                             combobox
8968                         ]
8969                     }
8970                     
8971                 ];
8972         } else if ( this.fieldLabel.length) {
8973                 Roo.log(" label");
8974                  cfg.cn = [
8975                    
8976                     {
8977                         tag: 'label',
8978                         //cls : 'input-group-addon',
8979                         html : this.fieldLabel
8980                         
8981                     },
8982                     
8983                     combobox
8984                     
8985                 ];
8986
8987         } else {
8988             
8989                 Roo.log(" no label && no align");
8990                 cfg = combobox
8991                      
8992                 
8993         }
8994          
8995         var settings=this;
8996         ['xs','sm','md','lg'].map(function(size){
8997             if (settings[size]) {
8998                 cfg.cls += ' col-' + size + '-' + settings[size];
8999             }
9000         });
9001         Roo.log(cfg);
9002         return cfg;
9003         
9004     },
9005     
9006     
9007     
9008     // private
9009     onResize : function(w, h){
9010 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9011 //        if(typeof w == 'number'){
9012 //            var x = w - this.trigger.getWidth();
9013 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9014 //            this.trigger.setStyle('left', x+'px');
9015 //        }
9016     },
9017
9018     // private
9019     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9020
9021     // private
9022     getResizeEl : function(){
9023         return this.inputEl();
9024     },
9025
9026     // private
9027     getPositionEl : function(){
9028         return this.inputEl();
9029     },
9030
9031     // private
9032     alignErrorIcon : function(){
9033         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9034     },
9035
9036     // private
9037     initEvents : function(){
9038         
9039         this.createList();
9040         
9041         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9042         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9043         if(!this.multiple && this.showToggleBtn){
9044             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9045             if(this.hideTrigger){
9046                 this.trigger.setDisplayed(false);
9047             }
9048             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9049         }
9050         
9051         if(this.multiple){
9052             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9053         }
9054         
9055         if(this.removable && !this.editable && !this.tickable){
9056             var close = this.closeTriggerEl();
9057             
9058             if(close){
9059                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9060                 close.on('click', this.removeBtnClick, this, close);
9061             }
9062         }
9063         
9064         //this.trigger.addClassOnOver('x-form-trigger-over');
9065         //this.trigger.addClassOnClick('x-form-trigger-click');
9066         
9067         //if(!this.width){
9068         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9069         //}
9070     },
9071     
9072     closeTriggerEl : function()
9073     {
9074         var close = this.el.select('.roo-combo-removable-btn', true).first();
9075         return close ? close : false;
9076     },
9077     
9078     removeBtnClick : function(e, h, el)
9079     {
9080         e.preventDefault();
9081         
9082         if(this.fireEvent("remove", this) !== false){
9083             this.reset();
9084         }
9085     },
9086     
9087     createList : function()
9088     {
9089         this.list = Roo.get(document.body).createChild({
9090             tag: 'ul',
9091             cls: 'typeahead typeahead-long dropdown-menu',
9092             style: 'display:none'
9093         });
9094         
9095         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9096         
9097     },
9098
9099     // private
9100     initTrigger : function(){
9101        
9102     },
9103
9104     // private
9105     onDestroy : function(){
9106         if(this.trigger){
9107             this.trigger.removeAllListeners();
9108           //  this.trigger.remove();
9109         }
9110         //if(this.wrap){
9111         //    this.wrap.remove();
9112         //}
9113         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9114     },
9115
9116     // private
9117     onFocus : function(){
9118         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9119         /*
9120         if(!this.mimicing){
9121             this.wrap.addClass('x-trigger-wrap-focus');
9122             this.mimicing = true;
9123             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9124             if(this.monitorTab){
9125                 this.el.on("keydown", this.checkTab, this);
9126             }
9127         }
9128         */
9129     },
9130
9131     // private
9132     checkTab : function(e){
9133         if(e.getKey() == e.TAB){
9134             this.triggerBlur();
9135         }
9136     },
9137
9138     // private
9139     onBlur : function(){
9140         // do nothing
9141     },
9142
9143     // private
9144     mimicBlur : function(e, t){
9145         /*
9146         if(!this.wrap.contains(t) && this.validateBlur()){
9147             this.triggerBlur();
9148         }
9149         */
9150     },
9151
9152     // private
9153     triggerBlur : function(){
9154         this.mimicing = false;
9155         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9156         if(this.monitorTab){
9157             this.el.un("keydown", this.checkTab, this);
9158         }
9159         //this.wrap.removeClass('x-trigger-wrap-focus');
9160         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9161     },
9162
9163     // private
9164     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9165     validateBlur : function(e, t){
9166         return true;
9167     },
9168
9169     // private
9170     onDisable : function(){
9171         this.inputEl().dom.disabled = true;
9172         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9173         //if(this.wrap){
9174         //    this.wrap.addClass('x-item-disabled');
9175         //}
9176     },
9177
9178     // private
9179     onEnable : function(){
9180         this.inputEl().dom.disabled = false;
9181         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9182         //if(this.wrap){
9183         //    this.el.removeClass('x-item-disabled');
9184         //}
9185     },
9186
9187     // private
9188     onShow : function(){
9189         var ae = this.getActionEl();
9190         
9191         if(ae){
9192             ae.dom.style.display = '';
9193             ae.dom.style.visibility = 'visible';
9194         }
9195     },
9196
9197     // private
9198     
9199     onHide : function(){
9200         var ae = this.getActionEl();
9201         ae.dom.style.display = 'none';
9202     },
9203
9204     /**
9205      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9206      * by an implementing function.
9207      * @method
9208      * @param {EventObject} e
9209      */
9210     onTriggerClick : Roo.emptyFn
9211 });
9212  /*
9213  * Based on:
9214  * Ext JS Library 1.1.1
9215  * Copyright(c) 2006-2007, Ext JS, LLC.
9216  *
9217  * Originally Released Under LGPL - original licence link has changed is not relivant.
9218  *
9219  * Fork - LGPL
9220  * <script type="text/javascript">
9221  */
9222
9223
9224 /**
9225  * @class Roo.data.SortTypes
9226  * @singleton
9227  * Defines the default sorting (casting?) comparison functions used when sorting data.
9228  */
9229 Roo.data.SortTypes = {
9230     /**
9231      * Default sort that does nothing
9232      * @param {Mixed} s The value being converted
9233      * @return {Mixed} The comparison value
9234      */
9235     none : function(s){
9236         return s;
9237     },
9238     
9239     /**
9240      * The regular expression used to strip tags
9241      * @type {RegExp}
9242      * @property
9243      */
9244     stripTagsRE : /<\/?[^>]+>/gi,
9245     
9246     /**
9247      * Strips all HTML tags to sort on text only
9248      * @param {Mixed} s The value being converted
9249      * @return {String} The comparison value
9250      */
9251     asText : function(s){
9252         return String(s).replace(this.stripTagsRE, "");
9253     },
9254     
9255     /**
9256      * Strips all HTML tags to sort on text only - Case insensitive
9257      * @param {Mixed} s The value being converted
9258      * @return {String} The comparison value
9259      */
9260     asUCText : function(s){
9261         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9262     },
9263     
9264     /**
9265      * Case insensitive string
9266      * @param {Mixed} s The value being converted
9267      * @return {String} The comparison value
9268      */
9269     asUCString : function(s) {
9270         return String(s).toUpperCase();
9271     },
9272     
9273     /**
9274      * Date sorting
9275      * @param {Mixed} s The value being converted
9276      * @return {Number} The comparison value
9277      */
9278     asDate : function(s) {
9279         if(!s){
9280             return 0;
9281         }
9282         if(s instanceof Date){
9283             return s.getTime();
9284         }
9285         return Date.parse(String(s));
9286     },
9287     
9288     /**
9289      * Float sorting
9290      * @param {Mixed} s The value being converted
9291      * @return {Float} The comparison value
9292      */
9293     asFloat : function(s) {
9294         var val = parseFloat(String(s).replace(/,/g, ""));
9295         if(isNaN(val)) val = 0;
9296         return val;
9297     },
9298     
9299     /**
9300      * Integer sorting
9301      * @param {Mixed} s The value being converted
9302      * @return {Number} The comparison value
9303      */
9304     asInt : function(s) {
9305         var val = parseInt(String(s).replace(/,/g, ""));
9306         if(isNaN(val)) val = 0;
9307         return val;
9308     }
9309 };/*
9310  * Based on:
9311  * Ext JS Library 1.1.1
9312  * Copyright(c) 2006-2007, Ext JS, LLC.
9313  *
9314  * Originally Released Under LGPL - original licence link has changed is not relivant.
9315  *
9316  * Fork - LGPL
9317  * <script type="text/javascript">
9318  */
9319
9320 /**
9321 * @class Roo.data.Record
9322  * Instances of this class encapsulate both record <em>definition</em> information, and record
9323  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9324  * to access Records cached in an {@link Roo.data.Store} object.<br>
9325  * <p>
9326  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9327  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9328  * objects.<br>
9329  * <p>
9330  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9331  * @constructor
9332  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9333  * {@link #create}. The parameters are the same.
9334  * @param {Array} data An associative Array of data values keyed by the field name.
9335  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9336  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9337  * not specified an integer id is generated.
9338  */
9339 Roo.data.Record = function(data, id){
9340     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9341     this.data = data;
9342 };
9343
9344 /**
9345  * Generate a constructor for a specific record layout.
9346  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9347  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9348  * Each field definition object may contain the following properties: <ul>
9349  * <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,
9350  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9351  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9352  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9353  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9354  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9355  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9356  * this may be omitted.</p></li>
9357  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9358  * <ul><li>auto (Default, implies no conversion)</li>
9359  * <li>string</li>
9360  * <li>int</li>
9361  * <li>float</li>
9362  * <li>boolean</li>
9363  * <li>date</li></ul></p></li>
9364  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9365  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9366  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9367  * by the Reader into an object that will be stored in the Record. It is passed the
9368  * following parameters:<ul>
9369  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9370  * </ul></p></li>
9371  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9372  * </ul>
9373  * <br>usage:<br><pre><code>
9374 var TopicRecord = Roo.data.Record.create(
9375     {name: 'title', mapping: 'topic_title'},
9376     {name: 'author', mapping: 'username'},
9377     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9378     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9379     {name: 'lastPoster', mapping: 'user2'},
9380     {name: 'excerpt', mapping: 'post_text'}
9381 );
9382
9383 var myNewRecord = new TopicRecord({
9384     title: 'Do my job please',
9385     author: 'noobie',
9386     totalPosts: 1,
9387     lastPost: new Date(),
9388     lastPoster: 'Animal',
9389     excerpt: 'No way dude!'
9390 });
9391 myStore.add(myNewRecord);
9392 </code></pre>
9393  * @method create
9394  * @static
9395  */
9396 Roo.data.Record.create = function(o){
9397     var f = function(){
9398         f.superclass.constructor.apply(this, arguments);
9399     };
9400     Roo.extend(f, Roo.data.Record);
9401     var p = f.prototype;
9402     p.fields = new Roo.util.MixedCollection(false, function(field){
9403         return field.name;
9404     });
9405     for(var i = 0, len = o.length; i < len; i++){
9406         p.fields.add(new Roo.data.Field(o[i]));
9407     }
9408     f.getField = function(name){
9409         return p.fields.get(name);  
9410     };
9411     return f;
9412 };
9413
9414 Roo.data.Record.AUTO_ID = 1000;
9415 Roo.data.Record.EDIT = 'edit';
9416 Roo.data.Record.REJECT = 'reject';
9417 Roo.data.Record.COMMIT = 'commit';
9418
9419 Roo.data.Record.prototype = {
9420     /**
9421      * Readonly flag - true if this record has been modified.
9422      * @type Boolean
9423      */
9424     dirty : false,
9425     editing : false,
9426     error: null,
9427     modified: null,
9428
9429     // private
9430     join : function(store){
9431         this.store = store;
9432     },
9433
9434     /**
9435      * Set the named field to the specified value.
9436      * @param {String} name The name of the field to set.
9437      * @param {Object} value The value to set the field to.
9438      */
9439     set : function(name, value){
9440         if(this.data[name] == value){
9441             return;
9442         }
9443         this.dirty = true;
9444         if(!this.modified){
9445             this.modified = {};
9446         }
9447         if(typeof this.modified[name] == 'undefined'){
9448             this.modified[name] = this.data[name];
9449         }
9450         this.data[name] = value;
9451         if(!this.editing && this.store){
9452             this.store.afterEdit(this);
9453         }       
9454     },
9455
9456     /**
9457      * Get the value of the named field.
9458      * @param {String} name The name of the field to get the value of.
9459      * @return {Object} The value of the field.
9460      */
9461     get : function(name){
9462         return this.data[name]; 
9463     },
9464
9465     // private
9466     beginEdit : function(){
9467         this.editing = true;
9468         this.modified = {}; 
9469     },
9470
9471     // private
9472     cancelEdit : function(){
9473         this.editing = false;
9474         delete this.modified;
9475     },
9476
9477     // private
9478     endEdit : function(){
9479         this.editing = false;
9480         if(this.dirty && this.store){
9481             this.store.afterEdit(this);
9482         }
9483     },
9484
9485     /**
9486      * Usually called by the {@link Roo.data.Store} which owns the Record.
9487      * Rejects all changes made to the Record since either creation, or the last commit operation.
9488      * Modified fields are reverted to their original values.
9489      * <p>
9490      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9491      * of reject operations.
9492      */
9493     reject : function(){
9494         var m = this.modified;
9495         for(var n in m){
9496             if(typeof m[n] != "function"){
9497                 this.data[n] = m[n];
9498             }
9499         }
9500         this.dirty = false;
9501         delete this.modified;
9502         this.editing = false;
9503         if(this.store){
9504             this.store.afterReject(this);
9505         }
9506     },
9507
9508     /**
9509      * Usually called by the {@link Roo.data.Store} which owns the Record.
9510      * Commits all changes made to the Record since either creation, or the last commit operation.
9511      * <p>
9512      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9513      * of commit operations.
9514      */
9515     commit : function(){
9516         this.dirty = false;
9517         delete this.modified;
9518         this.editing = false;
9519         if(this.store){
9520             this.store.afterCommit(this);
9521         }
9522     },
9523
9524     // private
9525     hasError : function(){
9526         return this.error != null;
9527     },
9528
9529     // private
9530     clearError : function(){
9531         this.error = null;
9532     },
9533
9534     /**
9535      * Creates a copy of this record.
9536      * @param {String} id (optional) A new record id if you don't want to use this record's id
9537      * @return {Record}
9538      */
9539     copy : function(newId) {
9540         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9541     }
9542 };/*
9543  * Based on:
9544  * Ext JS Library 1.1.1
9545  * Copyright(c) 2006-2007, Ext JS, LLC.
9546  *
9547  * Originally Released Under LGPL - original licence link has changed is not relivant.
9548  *
9549  * Fork - LGPL
9550  * <script type="text/javascript">
9551  */
9552
9553
9554
9555 /**
9556  * @class Roo.data.Store
9557  * @extends Roo.util.Observable
9558  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9559  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9560  * <p>
9561  * 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
9562  * has no knowledge of the format of the data returned by the Proxy.<br>
9563  * <p>
9564  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9565  * instances from the data object. These records are cached and made available through accessor functions.
9566  * @constructor
9567  * Creates a new Store.
9568  * @param {Object} config A config object containing the objects needed for the Store to access data,
9569  * and read the data into Records.
9570  */
9571 Roo.data.Store = function(config){
9572     this.data = new Roo.util.MixedCollection(false);
9573     this.data.getKey = function(o){
9574         return o.id;
9575     };
9576     this.baseParams = {};
9577     // private
9578     this.paramNames = {
9579         "start" : "start",
9580         "limit" : "limit",
9581         "sort" : "sort",
9582         "dir" : "dir",
9583         "multisort" : "_multisort"
9584     };
9585
9586     if(config && config.data){
9587         this.inlineData = config.data;
9588         delete config.data;
9589     }
9590
9591     Roo.apply(this, config);
9592     
9593     if(this.reader){ // reader passed
9594         this.reader = Roo.factory(this.reader, Roo.data);
9595         this.reader.xmodule = this.xmodule || false;
9596         if(!this.recordType){
9597             this.recordType = this.reader.recordType;
9598         }
9599         if(this.reader.onMetaChange){
9600             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9601         }
9602     }
9603
9604     if(this.recordType){
9605         this.fields = this.recordType.prototype.fields;
9606     }
9607     this.modified = [];
9608
9609     this.addEvents({
9610         /**
9611          * @event datachanged
9612          * Fires when the data cache has changed, and a widget which is using this Store
9613          * as a Record cache should refresh its view.
9614          * @param {Store} this
9615          */
9616         datachanged : true,
9617         /**
9618          * @event metachange
9619          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9620          * @param {Store} this
9621          * @param {Object} meta The JSON metadata
9622          */
9623         metachange : true,
9624         /**
9625          * @event add
9626          * Fires when Records have been added to the Store
9627          * @param {Store} this
9628          * @param {Roo.data.Record[]} records The array of Records added
9629          * @param {Number} index The index at which the record(s) were added
9630          */
9631         add : true,
9632         /**
9633          * @event remove
9634          * Fires when a Record has been removed from the Store
9635          * @param {Store} this
9636          * @param {Roo.data.Record} record The Record that was removed
9637          * @param {Number} index The index at which the record was removed
9638          */
9639         remove : true,
9640         /**
9641          * @event update
9642          * Fires when a Record has been updated
9643          * @param {Store} this
9644          * @param {Roo.data.Record} record The Record that was updated
9645          * @param {String} operation The update operation being performed.  Value may be one of:
9646          * <pre><code>
9647  Roo.data.Record.EDIT
9648  Roo.data.Record.REJECT
9649  Roo.data.Record.COMMIT
9650          * </code></pre>
9651          */
9652         update : true,
9653         /**
9654          * @event clear
9655          * Fires when the data cache has been cleared.
9656          * @param {Store} this
9657          */
9658         clear : true,
9659         /**
9660          * @event beforeload
9661          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9662          * the load action will be canceled.
9663          * @param {Store} this
9664          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9665          */
9666         beforeload : true,
9667         /**
9668          * @event beforeloadadd
9669          * Fires after a new set of Records has been loaded.
9670          * @param {Store} this
9671          * @param {Roo.data.Record[]} records The Records that were loaded
9672          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9673          */
9674         beforeloadadd : true,
9675         /**
9676          * @event load
9677          * Fires after a new set of Records has been loaded, before they are added to the store.
9678          * @param {Store} this
9679          * @param {Roo.data.Record[]} records The Records that were loaded
9680          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9681          * @params {Object} return from reader
9682          */
9683         load : true,
9684         /**
9685          * @event loadexception
9686          * Fires if an exception occurs in the Proxy during loading.
9687          * Called with the signature of the Proxy's "loadexception" event.
9688          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9689          * 
9690          * @param {Proxy} 
9691          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9692          * @param {Object} load options 
9693          * @param {Object} jsonData from your request (normally this contains the Exception)
9694          */
9695         loadexception : true
9696     });
9697     
9698     if(this.proxy){
9699         this.proxy = Roo.factory(this.proxy, Roo.data);
9700         this.proxy.xmodule = this.xmodule || false;
9701         this.relayEvents(this.proxy,  ["loadexception"]);
9702     }
9703     this.sortToggle = {};
9704     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9705
9706     Roo.data.Store.superclass.constructor.call(this);
9707
9708     if(this.inlineData){
9709         this.loadData(this.inlineData);
9710         delete this.inlineData;
9711     }
9712 };
9713
9714 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9715      /**
9716     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9717     * without a remote query - used by combo/forms at present.
9718     */
9719     
9720     /**
9721     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9722     */
9723     /**
9724     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9725     */
9726     /**
9727     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9728     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9729     */
9730     /**
9731     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9732     * on any HTTP request
9733     */
9734     /**
9735     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9736     */
9737     /**
9738     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9739     */
9740     multiSort: false,
9741     /**
9742     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9743     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9744     */
9745     remoteSort : false,
9746
9747     /**
9748     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9749      * loaded or when a record is removed. (defaults to false).
9750     */
9751     pruneModifiedRecords : false,
9752
9753     // private
9754     lastOptions : null,
9755
9756     /**
9757      * Add Records to the Store and fires the add event.
9758      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9759      */
9760     add : function(records){
9761         records = [].concat(records);
9762         for(var i = 0, len = records.length; i < len; i++){
9763             records[i].join(this);
9764         }
9765         var index = this.data.length;
9766         this.data.addAll(records);
9767         this.fireEvent("add", this, records, index);
9768     },
9769
9770     /**
9771      * Remove a Record from the Store and fires the remove event.
9772      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9773      */
9774     remove : function(record){
9775         var index = this.data.indexOf(record);
9776         this.data.removeAt(index);
9777         if(this.pruneModifiedRecords){
9778             this.modified.remove(record);
9779         }
9780         this.fireEvent("remove", this, record, index);
9781     },
9782
9783     /**
9784      * Remove all Records from the Store and fires the clear event.
9785      */
9786     removeAll : function(){
9787         this.data.clear();
9788         if(this.pruneModifiedRecords){
9789             this.modified = [];
9790         }
9791         this.fireEvent("clear", this);
9792     },
9793
9794     /**
9795      * Inserts Records to the Store at the given index and fires the add event.
9796      * @param {Number} index The start index at which to insert the passed Records.
9797      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9798      */
9799     insert : function(index, records){
9800         records = [].concat(records);
9801         for(var i = 0, len = records.length; i < len; i++){
9802             this.data.insert(index, records[i]);
9803             records[i].join(this);
9804         }
9805         this.fireEvent("add", this, records, index);
9806     },
9807
9808     /**
9809      * Get the index within the cache of the passed Record.
9810      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9811      * @return {Number} The index of the passed Record. Returns -1 if not found.
9812      */
9813     indexOf : function(record){
9814         return this.data.indexOf(record);
9815     },
9816
9817     /**
9818      * Get the index within the cache of the Record with the passed id.
9819      * @param {String} id The id of the Record to find.
9820      * @return {Number} The index of the Record. Returns -1 if not found.
9821      */
9822     indexOfId : function(id){
9823         return this.data.indexOfKey(id);
9824     },
9825
9826     /**
9827      * Get the Record with the specified id.
9828      * @param {String} id The id of the Record to find.
9829      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9830      */
9831     getById : function(id){
9832         return this.data.key(id);
9833     },
9834
9835     /**
9836      * Get the Record at the specified index.
9837      * @param {Number} index The index of the Record to find.
9838      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9839      */
9840     getAt : function(index){
9841         return this.data.itemAt(index);
9842     },
9843
9844     /**
9845      * Returns a range of Records between specified indices.
9846      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9847      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9848      * @return {Roo.data.Record[]} An array of Records
9849      */
9850     getRange : function(start, end){
9851         return this.data.getRange(start, end);
9852     },
9853
9854     // private
9855     storeOptions : function(o){
9856         o = Roo.apply({}, o);
9857         delete o.callback;
9858         delete o.scope;
9859         this.lastOptions = o;
9860     },
9861
9862     /**
9863      * Loads the Record cache from the configured Proxy using the configured Reader.
9864      * <p>
9865      * If using remote paging, then the first load call must specify the <em>start</em>
9866      * and <em>limit</em> properties in the options.params property to establish the initial
9867      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9868      * <p>
9869      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9870      * and this call will return before the new data has been loaded. Perform any post-processing
9871      * in a callback function, or in a "load" event handler.</strong>
9872      * <p>
9873      * @param {Object} options An object containing properties which control loading options:<ul>
9874      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9875      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9876      * passed the following arguments:<ul>
9877      * <li>r : Roo.data.Record[]</li>
9878      * <li>options: Options object from the load call</li>
9879      * <li>success: Boolean success indicator</li></ul></li>
9880      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9881      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9882      * </ul>
9883      */
9884     load : function(options){
9885         options = options || {};
9886         if(this.fireEvent("beforeload", this, options) !== false){
9887             this.storeOptions(options);
9888             var p = Roo.apply(options.params || {}, this.baseParams);
9889             // if meta was not loaded from remote source.. try requesting it.
9890             if (!this.reader.metaFromRemote) {
9891                 p._requestMeta = 1;
9892             }
9893             if(this.sortInfo && this.remoteSort){
9894                 var pn = this.paramNames;
9895                 p[pn["sort"]] = this.sortInfo.field;
9896                 p[pn["dir"]] = this.sortInfo.direction;
9897             }
9898             if (this.multiSort) {
9899                 var pn = this.paramNames;
9900                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9901             }
9902             
9903             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9904         }
9905     },
9906
9907     /**
9908      * Reloads the Record cache from the configured Proxy using the configured Reader and
9909      * the options from the last load operation performed.
9910      * @param {Object} options (optional) An object containing properties which may override the options
9911      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9912      * the most recently used options are reused).
9913      */
9914     reload : function(options){
9915         this.load(Roo.applyIf(options||{}, this.lastOptions));
9916     },
9917
9918     // private
9919     // Called as a callback by the Reader during a load operation.
9920     loadRecords : function(o, options, success){
9921         if(!o || success === false){
9922             if(success !== false){
9923                 this.fireEvent("load", this, [], options, o);
9924             }
9925             if(options.callback){
9926                 options.callback.call(options.scope || this, [], options, false);
9927             }
9928             return;
9929         }
9930         // if data returned failure - throw an exception.
9931         if (o.success === false) {
9932             // show a message if no listener is registered.
9933             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9934                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9935             }
9936             // loadmask wil be hooked into this..
9937             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9938             return;
9939         }
9940         var r = o.records, t = o.totalRecords || r.length;
9941         
9942         this.fireEvent("beforeloadadd", this, r, options, o);
9943         
9944         if(!options || options.add !== true){
9945             if(this.pruneModifiedRecords){
9946                 this.modified = [];
9947             }
9948             for(var i = 0, len = r.length; i < len; i++){
9949                 r[i].join(this);
9950             }
9951             if(this.snapshot){
9952                 this.data = this.snapshot;
9953                 delete this.snapshot;
9954             }
9955             this.data.clear();
9956             this.data.addAll(r);
9957             this.totalLength = t;
9958             this.applySort();
9959             this.fireEvent("datachanged", this);
9960         }else{
9961             this.totalLength = Math.max(t, this.data.length+r.length);
9962             this.add(r);
9963         }
9964         this.fireEvent("load", this, r, options, o);
9965         if(options.callback){
9966             options.callback.call(options.scope || this, r, options, true);
9967         }
9968     },
9969
9970
9971     /**
9972      * Loads data from a passed data block. A Reader which understands the format of the data
9973      * must have been configured in the constructor.
9974      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9975      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9976      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9977      */
9978     loadData : function(o, append){
9979         var r = this.reader.readRecords(o);
9980         this.loadRecords(r, {add: append}, true);
9981     },
9982
9983     /**
9984      * Gets the number of cached records.
9985      * <p>
9986      * <em>If using paging, this may not be the total size of the dataset. If the data object
9987      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9988      * the data set size</em>
9989      */
9990     getCount : function(){
9991         return this.data.length || 0;
9992     },
9993
9994     /**
9995      * Gets the total number of records in the dataset as returned by the server.
9996      * <p>
9997      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9998      * the dataset size</em>
9999      */
10000     getTotalCount : function(){
10001         return this.totalLength || 0;
10002     },
10003
10004     /**
10005      * Returns the sort state of the Store as an object with two properties:
10006      * <pre><code>
10007  field {String} The name of the field by which the Records are sorted
10008  direction {String} The sort order, "ASC" or "DESC"
10009      * </code></pre>
10010      */
10011     getSortState : function(){
10012         return this.sortInfo;
10013     },
10014
10015     // private
10016     applySort : function(){
10017         if(this.sortInfo && !this.remoteSort){
10018             var s = this.sortInfo, f = s.field;
10019             var st = this.fields.get(f).sortType;
10020             var fn = function(r1, r2){
10021                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10022                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10023             };
10024             this.data.sort(s.direction, fn);
10025             if(this.snapshot && this.snapshot != this.data){
10026                 this.snapshot.sort(s.direction, fn);
10027             }
10028         }
10029     },
10030
10031     /**
10032      * Sets the default sort column and order to be used by the next load operation.
10033      * @param {String} fieldName The name of the field to sort by.
10034      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10035      */
10036     setDefaultSort : function(field, dir){
10037         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10038     },
10039
10040     /**
10041      * Sort the Records.
10042      * If remote sorting is used, the sort is performed on the server, and the cache is
10043      * reloaded. If local sorting is used, the cache is sorted internally.
10044      * @param {String} fieldName The name of the field to sort by.
10045      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10046      */
10047     sort : function(fieldName, dir){
10048         var f = this.fields.get(fieldName);
10049         if(!dir){
10050             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10051             
10052             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10053                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10054             }else{
10055                 dir = f.sortDir;
10056             }
10057         }
10058         this.sortToggle[f.name] = dir;
10059         this.sortInfo = {field: f.name, direction: dir};
10060         if(!this.remoteSort){
10061             this.applySort();
10062             this.fireEvent("datachanged", this);
10063         }else{
10064             this.load(this.lastOptions);
10065         }
10066     },
10067
10068     /**
10069      * Calls the specified function for each of the Records in the cache.
10070      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10071      * Returning <em>false</em> aborts and exits the iteration.
10072      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10073      */
10074     each : function(fn, scope){
10075         this.data.each(fn, scope);
10076     },
10077
10078     /**
10079      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10080      * (e.g., during paging).
10081      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10082      */
10083     getModifiedRecords : function(){
10084         return this.modified;
10085     },
10086
10087     // private
10088     createFilterFn : function(property, value, anyMatch){
10089         if(!value.exec){ // not a regex
10090             value = String(value);
10091             if(value.length == 0){
10092                 return false;
10093             }
10094             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10095         }
10096         return function(r){
10097             return value.test(r.data[property]);
10098         };
10099     },
10100
10101     /**
10102      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10103      * @param {String} property A field on your records
10104      * @param {Number} start The record index to start at (defaults to 0)
10105      * @param {Number} end The last record index to include (defaults to length - 1)
10106      * @return {Number} The sum
10107      */
10108     sum : function(property, start, end){
10109         var rs = this.data.items, v = 0;
10110         start = start || 0;
10111         end = (end || end === 0) ? end : rs.length-1;
10112
10113         for(var i = start; i <= end; i++){
10114             v += (rs[i].data[property] || 0);
10115         }
10116         return v;
10117     },
10118
10119     /**
10120      * Filter the records by a specified property.
10121      * @param {String} field A field on your records
10122      * @param {String/RegExp} value Either a string that the field
10123      * should start with or a RegExp to test against the field
10124      * @param {Boolean} anyMatch True to match any part not just the beginning
10125      */
10126     filter : function(property, value, anyMatch){
10127         var fn = this.createFilterFn(property, value, anyMatch);
10128         return fn ? this.filterBy(fn) : this.clearFilter();
10129     },
10130
10131     /**
10132      * Filter by a function. The specified function will be called with each
10133      * record in this data source. If the function returns true the record is included,
10134      * otherwise it is filtered.
10135      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10136      * @param {Object} scope (optional) The scope of the function (defaults to this)
10137      */
10138     filterBy : function(fn, scope){
10139         this.snapshot = this.snapshot || this.data;
10140         this.data = this.queryBy(fn, scope||this);
10141         this.fireEvent("datachanged", this);
10142     },
10143
10144     /**
10145      * Query the records by a specified property.
10146      * @param {String} field A field on your records
10147      * @param {String/RegExp} value Either a string that the field
10148      * should start with or a RegExp to test against the field
10149      * @param {Boolean} anyMatch True to match any part not just the beginning
10150      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10151      */
10152     query : function(property, value, anyMatch){
10153         var fn = this.createFilterFn(property, value, anyMatch);
10154         return fn ? this.queryBy(fn) : this.data.clone();
10155     },
10156
10157     /**
10158      * Query by a function. The specified function will be called with each
10159      * record in this data source. If the function returns true the record is included
10160      * in the results.
10161      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10162      * @param {Object} scope (optional) The scope of the function (defaults to this)
10163       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10164      **/
10165     queryBy : function(fn, scope){
10166         var data = this.snapshot || this.data;
10167         return data.filterBy(fn, scope||this);
10168     },
10169
10170     /**
10171      * Collects unique values for a particular dataIndex from this store.
10172      * @param {String} dataIndex The property to collect
10173      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10174      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10175      * @return {Array} An array of the unique values
10176      **/
10177     collect : function(dataIndex, allowNull, bypassFilter){
10178         var d = (bypassFilter === true && this.snapshot) ?
10179                 this.snapshot.items : this.data.items;
10180         var v, sv, r = [], l = {};
10181         for(var i = 0, len = d.length; i < len; i++){
10182             v = d[i].data[dataIndex];
10183             sv = String(v);
10184             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10185                 l[sv] = true;
10186                 r[r.length] = v;
10187             }
10188         }
10189         return r;
10190     },
10191
10192     /**
10193      * Revert to a view of the Record cache with no filtering applied.
10194      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10195      */
10196     clearFilter : function(suppressEvent){
10197         if(this.snapshot && this.snapshot != this.data){
10198             this.data = this.snapshot;
10199             delete this.snapshot;
10200             if(suppressEvent !== true){
10201                 this.fireEvent("datachanged", this);
10202             }
10203         }
10204     },
10205
10206     // private
10207     afterEdit : function(record){
10208         if(this.modified.indexOf(record) == -1){
10209             this.modified.push(record);
10210         }
10211         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10212     },
10213     
10214     // private
10215     afterReject : function(record){
10216         this.modified.remove(record);
10217         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10218     },
10219
10220     // private
10221     afterCommit : function(record){
10222         this.modified.remove(record);
10223         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10224     },
10225
10226     /**
10227      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10228      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10229      */
10230     commitChanges : function(){
10231         var m = this.modified.slice(0);
10232         this.modified = [];
10233         for(var i = 0, len = m.length; i < len; i++){
10234             m[i].commit();
10235         }
10236     },
10237
10238     /**
10239      * Cancel outstanding changes on all changed records.
10240      */
10241     rejectChanges : function(){
10242         var m = this.modified.slice(0);
10243         this.modified = [];
10244         for(var i = 0, len = m.length; i < len; i++){
10245             m[i].reject();
10246         }
10247     },
10248
10249     onMetaChange : function(meta, rtype, o){
10250         this.recordType = rtype;
10251         this.fields = rtype.prototype.fields;
10252         delete this.snapshot;
10253         this.sortInfo = meta.sortInfo || this.sortInfo;
10254         this.modified = [];
10255         this.fireEvent('metachange', this, this.reader.meta);
10256     },
10257     
10258     moveIndex : function(data, type)
10259     {
10260         var index = this.indexOf(data);
10261         
10262         var newIndex = index + type;
10263         
10264         this.remove(data);
10265         
10266         this.insert(newIndex, data);
10267         
10268     }
10269 });/*
10270  * Based on:
10271  * Ext JS Library 1.1.1
10272  * Copyright(c) 2006-2007, Ext JS, LLC.
10273  *
10274  * Originally Released Under LGPL - original licence link has changed is not relivant.
10275  *
10276  * Fork - LGPL
10277  * <script type="text/javascript">
10278  */
10279
10280 /**
10281  * @class Roo.data.SimpleStore
10282  * @extends Roo.data.Store
10283  * Small helper class to make creating Stores from Array data easier.
10284  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10285  * @cfg {Array} fields An array of field definition objects, or field name strings.
10286  * @cfg {Array} data The multi-dimensional array of data
10287  * @constructor
10288  * @param {Object} config
10289  */
10290 Roo.data.SimpleStore = function(config){
10291     Roo.data.SimpleStore.superclass.constructor.call(this, {
10292         isLocal : true,
10293         reader: new Roo.data.ArrayReader({
10294                 id: config.id
10295             },
10296             Roo.data.Record.create(config.fields)
10297         ),
10298         proxy : new Roo.data.MemoryProxy(config.data)
10299     });
10300     this.load();
10301 };
10302 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10303  * Based on:
10304  * Ext JS Library 1.1.1
10305  * Copyright(c) 2006-2007, Ext JS, LLC.
10306  *
10307  * Originally Released Under LGPL - original licence link has changed is not relivant.
10308  *
10309  * Fork - LGPL
10310  * <script type="text/javascript">
10311  */
10312
10313 /**
10314 /**
10315  * @extends Roo.data.Store
10316  * @class Roo.data.JsonStore
10317  * Small helper class to make creating Stores for JSON data easier. <br/>
10318 <pre><code>
10319 var store = new Roo.data.JsonStore({
10320     url: 'get-images.php',
10321     root: 'images',
10322     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10323 });
10324 </code></pre>
10325  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10326  * JsonReader and HttpProxy (unless inline data is provided).</b>
10327  * @cfg {Array} fields An array of field definition objects, or field name strings.
10328  * @constructor
10329  * @param {Object} config
10330  */
10331 Roo.data.JsonStore = function(c){
10332     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10333         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10334         reader: new Roo.data.JsonReader(c, c.fields)
10335     }));
10336 };
10337 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10338  * Based on:
10339  * Ext JS Library 1.1.1
10340  * Copyright(c) 2006-2007, Ext JS, LLC.
10341  *
10342  * Originally Released Under LGPL - original licence link has changed is not relivant.
10343  *
10344  * Fork - LGPL
10345  * <script type="text/javascript">
10346  */
10347
10348  
10349 Roo.data.Field = function(config){
10350     if(typeof config == "string"){
10351         config = {name: config};
10352     }
10353     Roo.apply(this, config);
10354     
10355     if(!this.type){
10356         this.type = "auto";
10357     }
10358     
10359     var st = Roo.data.SortTypes;
10360     // named sortTypes are supported, here we look them up
10361     if(typeof this.sortType == "string"){
10362         this.sortType = st[this.sortType];
10363     }
10364     
10365     // set default sortType for strings and dates
10366     if(!this.sortType){
10367         switch(this.type){
10368             case "string":
10369                 this.sortType = st.asUCString;
10370                 break;
10371             case "date":
10372                 this.sortType = st.asDate;
10373                 break;
10374             default:
10375                 this.sortType = st.none;
10376         }
10377     }
10378
10379     // define once
10380     var stripRe = /[\$,%]/g;
10381
10382     // prebuilt conversion function for this field, instead of
10383     // switching every time we're reading a value
10384     if(!this.convert){
10385         var cv, dateFormat = this.dateFormat;
10386         switch(this.type){
10387             case "":
10388             case "auto":
10389             case undefined:
10390                 cv = function(v){ return v; };
10391                 break;
10392             case "string":
10393                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10394                 break;
10395             case "int":
10396                 cv = function(v){
10397                     return v !== undefined && v !== null && v !== '' ?
10398                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10399                     };
10400                 break;
10401             case "float":
10402                 cv = function(v){
10403                     return v !== undefined && v !== null && v !== '' ?
10404                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10405                     };
10406                 break;
10407             case "bool":
10408             case "boolean":
10409                 cv = function(v){ return v === true || v === "true" || v == 1; };
10410                 break;
10411             case "date":
10412                 cv = function(v){
10413                     if(!v){
10414                         return '';
10415                     }
10416                     if(v instanceof Date){
10417                         return v;
10418                     }
10419                     if(dateFormat){
10420                         if(dateFormat == "timestamp"){
10421                             return new Date(v*1000);
10422                         }
10423                         return Date.parseDate(v, dateFormat);
10424                     }
10425                     var parsed = Date.parse(v);
10426                     return parsed ? new Date(parsed) : null;
10427                 };
10428              break;
10429             
10430         }
10431         this.convert = cv;
10432     }
10433 };
10434
10435 Roo.data.Field.prototype = {
10436     dateFormat: null,
10437     defaultValue: "",
10438     mapping: null,
10439     sortType : null,
10440     sortDir : "ASC"
10441 };/*
10442  * Based on:
10443  * Ext JS Library 1.1.1
10444  * Copyright(c) 2006-2007, Ext JS, LLC.
10445  *
10446  * Originally Released Under LGPL - original licence link has changed is not relivant.
10447  *
10448  * Fork - LGPL
10449  * <script type="text/javascript">
10450  */
10451  
10452 // Base class for reading structured data from a data source.  This class is intended to be
10453 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10454
10455 /**
10456  * @class Roo.data.DataReader
10457  * Base class for reading structured data from a data source.  This class is intended to be
10458  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10459  */
10460
10461 Roo.data.DataReader = function(meta, recordType){
10462     
10463     this.meta = meta;
10464     
10465     this.recordType = recordType instanceof Array ? 
10466         Roo.data.Record.create(recordType) : recordType;
10467 };
10468
10469 Roo.data.DataReader.prototype = {
10470      /**
10471      * Create an empty record
10472      * @param {Object} data (optional) - overlay some values
10473      * @return {Roo.data.Record} record created.
10474      */
10475     newRow :  function(d) {
10476         var da =  {};
10477         this.recordType.prototype.fields.each(function(c) {
10478             switch( c.type) {
10479                 case 'int' : da[c.name] = 0; break;
10480                 case 'date' : da[c.name] = new Date(); break;
10481                 case 'float' : da[c.name] = 0.0; break;
10482                 case 'boolean' : da[c.name] = false; break;
10483                 default : da[c.name] = ""; break;
10484             }
10485             
10486         });
10487         return new this.recordType(Roo.apply(da, d));
10488     }
10489     
10490 };/*
10491  * Based on:
10492  * Ext JS Library 1.1.1
10493  * Copyright(c) 2006-2007, Ext JS, LLC.
10494  *
10495  * Originally Released Under LGPL - original licence link has changed is not relivant.
10496  *
10497  * Fork - LGPL
10498  * <script type="text/javascript">
10499  */
10500
10501 /**
10502  * @class Roo.data.DataProxy
10503  * @extends Roo.data.Observable
10504  * This class is an abstract base class for implementations which provide retrieval of
10505  * unformatted data objects.<br>
10506  * <p>
10507  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10508  * (of the appropriate type which knows how to parse the data object) to provide a block of
10509  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10510  * <p>
10511  * Custom implementations must implement the load method as described in
10512  * {@link Roo.data.HttpProxy#load}.
10513  */
10514 Roo.data.DataProxy = function(){
10515     this.addEvents({
10516         /**
10517          * @event beforeload
10518          * Fires before a network request is made to retrieve a data object.
10519          * @param {Object} This DataProxy object.
10520          * @param {Object} params The params parameter to the load function.
10521          */
10522         beforeload : true,
10523         /**
10524          * @event load
10525          * Fires before the load method's callback is called.
10526          * @param {Object} This DataProxy object.
10527          * @param {Object} o The data object.
10528          * @param {Object} arg The callback argument object passed to the load function.
10529          */
10530         load : true,
10531         /**
10532          * @event loadexception
10533          * Fires if an Exception occurs during data retrieval.
10534          * @param {Object} This DataProxy object.
10535          * @param {Object} o The data object.
10536          * @param {Object} arg The callback argument object passed to the load function.
10537          * @param {Object} e The Exception.
10538          */
10539         loadexception : true
10540     });
10541     Roo.data.DataProxy.superclass.constructor.call(this);
10542 };
10543
10544 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10545
10546     /**
10547      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10548      */
10549 /*
10550  * Based on:
10551  * Ext JS Library 1.1.1
10552  * Copyright(c) 2006-2007, Ext JS, LLC.
10553  *
10554  * Originally Released Under LGPL - original licence link has changed is not relivant.
10555  *
10556  * Fork - LGPL
10557  * <script type="text/javascript">
10558  */
10559 /**
10560  * @class Roo.data.MemoryProxy
10561  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10562  * to the Reader when its load method is called.
10563  * @constructor
10564  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10565  */
10566 Roo.data.MemoryProxy = function(data){
10567     if (data.data) {
10568         data = data.data;
10569     }
10570     Roo.data.MemoryProxy.superclass.constructor.call(this);
10571     this.data = data;
10572 };
10573
10574 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10575     /**
10576      * Load data from the requested source (in this case an in-memory
10577      * data object passed to the constructor), read the data object into
10578      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10579      * process that block using the passed callback.
10580      * @param {Object} params This parameter is not used by the MemoryProxy class.
10581      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10582      * object into a block of Roo.data.Records.
10583      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10584      * The function must be passed <ul>
10585      * <li>The Record block object</li>
10586      * <li>The "arg" argument from the load function</li>
10587      * <li>A boolean success indicator</li>
10588      * </ul>
10589      * @param {Object} scope The scope in which to call the callback
10590      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10591      */
10592     load : function(params, reader, callback, scope, arg){
10593         params = params || {};
10594         var result;
10595         try {
10596             result = reader.readRecords(this.data);
10597         }catch(e){
10598             this.fireEvent("loadexception", this, arg, null, e);
10599             callback.call(scope, null, arg, false);
10600             return;
10601         }
10602         callback.call(scope, result, arg, true);
10603     },
10604     
10605     // private
10606     update : function(params, records){
10607         
10608     }
10609 });/*
10610  * Based on:
10611  * Ext JS Library 1.1.1
10612  * Copyright(c) 2006-2007, Ext JS, LLC.
10613  *
10614  * Originally Released Under LGPL - original licence link has changed is not relivant.
10615  *
10616  * Fork - LGPL
10617  * <script type="text/javascript">
10618  */
10619 /**
10620  * @class Roo.data.HttpProxy
10621  * @extends Roo.data.DataProxy
10622  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10623  * configured to reference a certain URL.<br><br>
10624  * <p>
10625  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10626  * from which the running page was served.<br><br>
10627  * <p>
10628  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10629  * <p>
10630  * Be aware that to enable the browser to parse an XML document, the server must set
10631  * the Content-Type header in the HTTP response to "text/xml".
10632  * @constructor
10633  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10634  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10635  * will be used to make the request.
10636  */
10637 Roo.data.HttpProxy = function(conn){
10638     Roo.data.HttpProxy.superclass.constructor.call(this);
10639     // is conn a conn config or a real conn?
10640     this.conn = conn;
10641     this.useAjax = !conn || !conn.events;
10642   
10643 };
10644
10645 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10646     // thse are take from connection...
10647     
10648     /**
10649      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10650      */
10651     /**
10652      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10653      * extra parameters to each request made by this object. (defaults to undefined)
10654      */
10655     /**
10656      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10657      *  to each request made by this object. (defaults to undefined)
10658      */
10659     /**
10660      * @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)
10661      */
10662     /**
10663      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10664      */
10665      /**
10666      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10667      * @type Boolean
10668      */
10669   
10670
10671     /**
10672      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10673      * @type Boolean
10674      */
10675     /**
10676      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10677      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10678      * a finer-grained basis than the DataProxy events.
10679      */
10680     getConnection : function(){
10681         return this.useAjax ? Roo.Ajax : this.conn;
10682     },
10683
10684     /**
10685      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10686      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10687      * process that block using the passed callback.
10688      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10689      * for the request to the remote server.
10690      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10691      * object into a block of Roo.data.Records.
10692      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10693      * The function must be passed <ul>
10694      * <li>The Record block object</li>
10695      * <li>The "arg" argument from the load function</li>
10696      * <li>A boolean success indicator</li>
10697      * </ul>
10698      * @param {Object} scope The scope in which to call the callback
10699      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10700      */
10701     load : function(params, reader, callback, scope, arg){
10702         if(this.fireEvent("beforeload", this, params) !== false){
10703             var  o = {
10704                 params : params || {},
10705                 request: {
10706                     callback : callback,
10707                     scope : scope,
10708                     arg : arg
10709                 },
10710                 reader: reader,
10711                 callback : this.loadResponse,
10712                 scope: this
10713             };
10714             if(this.useAjax){
10715                 Roo.applyIf(o, this.conn);
10716                 if(this.activeRequest){
10717                     Roo.Ajax.abort(this.activeRequest);
10718                 }
10719                 this.activeRequest = Roo.Ajax.request(o);
10720             }else{
10721                 this.conn.request(o);
10722             }
10723         }else{
10724             callback.call(scope||this, null, arg, false);
10725         }
10726     },
10727
10728     // private
10729     loadResponse : function(o, success, response){
10730         delete this.activeRequest;
10731         if(!success){
10732             this.fireEvent("loadexception", this, o, response);
10733             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10734             return;
10735         }
10736         var result;
10737         try {
10738             result = o.reader.read(response);
10739         }catch(e){
10740             this.fireEvent("loadexception", this, o, response, e);
10741             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10742             return;
10743         }
10744         
10745         this.fireEvent("load", this, o, o.request.arg);
10746         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10747     },
10748
10749     // private
10750     update : function(dataSet){
10751
10752     },
10753
10754     // private
10755     updateResponse : function(dataSet){
10756
10757     }
10758 });/*
10759  * Based on:
10760  * Ext JS Library 1.1.1
10761  * Copyright(c) 2006-2007, Ext JS, LLC.
10762  *
10763  * Originally Released Under LGPL - original licence link has changed is not relivant.
10764  *
10765  * Fork - LGPL
10766  * <script type="text/javascript">
10767  */
10768
10769 /**
10770  * @class Roo.data.ScriptTagProxy
10771  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10772  * other than the originating domain of the running page.<br><br>
10773  * <p>
10774  * <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
10775  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10776  * <p>
10777  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10778  * source code that is used as the source inside a &lt;script> tag.<br><br>
10779  * <p>
10780  * In order for the browser to process the returned data, the server must wrap the data object
10781  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10782  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10783  * depending on whether the callback name was passed:
10784  * <p>
10785  * <pre><code>
10786 boolean scriptTag = false;
10787 String cb = request.getParameter("callback");
10788 if (cb != null) {
10789     scriptTag = true;
10790     response.setContentType("text/javascript");
10791 } else {
10792     response.setContentType("application/x-json");
10793 }
10794 Writer out = response.getWriter();
10795 if (scriptTag) {
10796     out.write(cb + "(");
10797 }
10798 out.print(dataBlock.toJsonString());
10799 if (scriptTag) {
10800     out.write(");");
10801 }
10802 </pre></code>
10803  *
10804  * @constructor
10805  * @param {Object} config A configuration object.
10806  */
10807 Roo.data.ScriptTagProxy = function(config){
10808     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10809     Roo.apply(this, config);
10810     this.head = document.getElementsByTagName("head")[0];
10811 };
10812
10813 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10814
10815 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10816     /**
10817      * @cfg {String} url The URL from which to request the data object.
10818      */
10819     /**
10820      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10821      */
10822     timeout : 30000,
10823     /**
10824      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10825      * the server the name of the callback function set up by the load call to process the returned data object.
10826      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10827      * javascript output which calls this named function passing the data object as its only parameter.
10828      */
10829     callbackParam : "callback",
10830     /**
10831      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10832      * name to the request.
10833      */
10834     nocache : true,
10835
10836     /**
10837      * Load data from the configured URL, read the data object into
10838      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10839      * process that block using the passed callback.
10840      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10841      * for the request to the remote server.
10842      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10843      * object into a block of Roo.data.Records.
10844      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10845      * The function must be passed <ul>
10846      * <li>The Record block object</li>
10847      * <li>The "arg" argument from the load function</li>
10848      * <li>A boolean success indicator</li>
10849      * </ul>
10850      * @param {Object} scope The scope in which to call the callback
10851      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10852      */
10853     load : function(params, reader, callback, scope, arg){
10854         if(this.fireEvent("beforeload", this, params) !== false){
10855
10856             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10857
10858             var url = this.url;
10859             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10860             if(this.nocache){
10861                 url += "&_dc=" + (new Date().getTime());
10862             }
10863             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10864             var trans = {
10865                 id : transId,
10866                 cb : "stcCallback"+transId,
10867                 scriptId : "stcScript"+transId,
10868                 params : params,
10869                 arg : arg,
10870                 url : url,
10871                 callback : callback,
10872                 scope : scope,
10873                 reader : reader
10874             };
10875             var conn = this;
10876
10877             window[trans.cb] = function(o){
10878                 conn.handleResponse(o, trans);
10879             };
10880
10881             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10882
10883             if(this.autoAbort !== false){
10884                 this.abort();
10885             }
10886
10887             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10888
10889             var script = document.createElement("script");
10890             script.setAttribute("src", url);
10891             script.setAttribute("type", "text/javascript");
10892             script.setAttribute("id", trans.scriptId);
10893             this.head.appendChild(script);
10894
10895             this.trans = trans;
10896         }else{
10897             callback.call(scope||this, null, arg, false);
10898         }
10899     },
10900
10901     // private
10902     isLoading : function(){
10903         return this.trans ? true : false;
10904     },
10905
10906     /**
10907      * Abort the current server request.
10908      */
10909     abort : function(){
10910         if(this.isLoading()){
10911             this.destroyTrans(this.trans);
10912         }
10913     },
10914
10915     // private
10916     destroyTrans : function(trans, isLoaded){
10917         this.head.removeChild(document.getElementById(trans.scriptId));
10918         clearTimeout(trans.timeoutId);
10919         if(isLoaded){
10920             window[trans.cb] = undefined;
10921             try{
10922                 delete window[trans.cb];
10923             }catch(e){}
10924         }else{
10925             // if hasn't been loaded, wait for load to remove it to prevent script error
10926             window[trans.cb] = function(){
10927                 window[trans.cb] = undefined;
10928                 try{
10929                     delete window[trans.cb];
10930                 }catch(e){}
10931             };
10932         }
10933     },
10934
10935     // private
10936     handleResponse : function(o, trans){
10937         this.trans = false;
10938         this.destroyTrans(trans, true);
10939         var result;
10940         try {
10941             result = trans.reader.readRecords(o);
10942         }catch(e){
10943             this.fireEvent("loadexception", this, o, trans.arg, e);
10944             trans.callback.call(trans.scope||window, null, trans.arg, false);
10945             return;
10946         }
10947         this.fireEvent("load", this, o, trans.arg);
10948         trans.callback.call(trans.scope||window, result, trans.arg, true);
10949     },
10950
10951     // private
10952     handleFailure : function(trans){
10953         this.trans = false;
10954         this.destroyTrans(trans, false);
10955         this.fireEvent("loadexception", this, null, trans.arg);
10956         trans.callback.call(trans.scope||window, null, trans.arg, false);
10957     }
10958 });/*
10959  * Based on:
10960  * Ext JS Library 1.1.1
10961  * Copyright(c) 2006-2007, Ext JS, LLC.
10962  *
10963  * Originally Released Under LGPL - original licence link has changed is not relivant.
10964  *
10965  * Fork - LGPL
10966  * <script type="text/javascript">
10967  */
10968
10969 /**
10970  * @class Roo.data.JsonReader
10971  * @extends Roo.data.DataReader
10972  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10973  * based on mappings in a provided Roo.data.Record constructor.
10974  * 
10975  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10976  * in the reply previously. 
10977  * 
10978  * <p>
10979  * Example code:
10980  * <pre><code>
10981 var RecordDef = Roo.data.Record.create([
10982     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10983     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10984 ]);
10985 var myReader = new Roo.data.JsonReader({
10986     totalProperty: "results",    // The property which contains the total dataset size (optional)
10987     root: "rows",                // The property which contains an Array of row objects
10988     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10989 }, RecordDef);
10990 </code></pre>
10991  * <p>
10992  * This would consume a JSON file like this:
10993  * <pre><code>
10994 { 'results': 2, 'rows': [
10995     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10996     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10997 }
10998 </code></pre>
10999  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11000  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11001  * paged from the remote server.
11002  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11003  * @cfg {String} root name of the property which contains the Array of row objects.
11004  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11005  * @cfg {Array} fields Array of field definition objects
11006  * @constructor
11007  * Create a new JsonReader
11008  * @param {Object} meta Metadata configuration options
11009  * @param {Object} recordType Either an Array of field definition objects,
11010  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11011  */
11012 Roo.data.JsonReader = function(meta, recordType){
11013     
11014     meta = meta || {};
11015     // set some defaults:
11016     Roo.applyIf(meta, {
11017         totalProperty: 'total',
11018         successProperty : 'success',
11019         root : 'data',
11020         id : 'id'
11021     });
11022     
11023     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11024 };
11025 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11026     
11027     /**
11028      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11029      * Used by Store query builder to append _requestMeta to params.
11030      * 
11031      */
11032     metaFromRemote : false,
11033     /**
11034      * This method is only used by a DataProxy which has retrieved data from a remote server.
11035      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11036      * @return {Object} data A data block which is used by an Roo.data.Store object as
11037      * a cache of Roo.data.Records.
11038      */
11039     read : function(response){
11040         var json = response.responseText;
11041        
11042         var o = /* eval:var:o */ eval("("+json+")");
11043         if(!o) {
11044             throw {message: "JsonReader.read: Json object not found"};
11045         }
11046         
11047         if(o.metaData){
11048             
11049             delete this.ef;
11050             this.metaFromRemote = true;
11051             this.meta = o.metaData;
11052             this.recordType = Roo.data.Record.create(o.metaData.fields);
11053             this.onMetaChange(this.meta, this.recordType, o);
11054         }
11055         return this.readRecords(o);
11056     },
11057
11058     // private function a store will implement
11059     onMetaChange : function(meta, recordType, o){
11060
11061     },
11062
11063     /**
11064          * @ignore
11065          */
11066     simpleAccess: function(obj, subsc) {
11067         return obj[subsc];
11068     },
11069
11070         /**
11071          * @ignore
11072          */
11073     getJsonAccessor: function(){
11074         var re = /[\[\.]/;
11075         return function(expr) {
11076             try {
11077                 return(re.test(expr))
11078                     ? new Function("obj", "return obj." + expr)
11079                     : function(obj){
11080                         return obj[expr];
11081                     };
11082             } catch(e){}
11083             return Roo.emptyFn;
11084         };
11085     }(),
11086
11087     /**
11088      * Create a data block containing Roo.data.Records from an XML document.
11089      * @param {Object} o An object which contains an Array of row objects in the property specified
11090      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11091      * which contains the total size of the dataset.
11092      * @return {Object} data A data block which is used by an Roo.data.Store object as
11093      * a cache of Roo.data.Records.
11094      */
11095     readRecords : function(o){
11096         /**
11097          * After any data loads, the raw JSON data is available for further custom processing.
11098          * @type Object
11099          */
11100         this.o = o;
11101         var s = this.meta, Record = this.recordType,
11102             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11103
11104 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11105         if (!this.ef) {
11106             if(s.totalProperty) {
11107                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11108                 }
11109                 if(s.successProperty) {
11110                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11111                 }
11112                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11113                 if (s.id) {
11114                         var g = this.getJsonAccessor(s.id);
11115                         this.getId = function(rec) {
11116                                 var r = g(rec);  
11117                                 return (r === undefined || r === "") ? null : r;
11118                         };
11119                 } else {
11120                         this.getId = function(){return null;};
11121                 }
11122             this.ef = [];
11123             for(var jj = 0; jj < fl; jj++){
11124                 f = fi[jj];
11125                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11126                 this.ef[jj] = this.getJsonAccessor(map);
11127             }
11128         }
11129
11130         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11131         if(s.totalProperty){
11132             var vt = parseInt(this.getTotal(o), 10);
11133             if(!isNaN(vt)){
11134                 totalRecords = vt;
11135             }
11136         }
11137         if(s.successProperty){
11138             var vs = this.getSuccess(o);
11139             if(vs === false || vs === 'false'){
11140                 success = false;
11141             }
11142         }
11143         var records = [];
11144         for(var i = 0; i < c; i++){
11145                 var n = root[i];
11146             var values = {};
11147             var id = this.getId(n);
11148             for(var j = 0; j < fl; j++){
11149                 f = fi[j];
11150             var v = this.ef[j](n);
11151             if (!f.convert) {
11152                 Roo.log('missing convert for ' + f.name);
11153                 Roo.log(f);
11154                 continue;
11155             }
11156             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11157             }
11158             var record = new Record(values, id);
11159             record.json = n;
11160             records[i] = record;
11161         }
11162         return {
11163             raw : o,
11164             success : success,
11165             records : records,
11166             totalRecords : totalRecords
11167         };
11168     }
11169 });/*
11170  * Based on:
11171  * Ext JS Library 1.1.1
11172  * Copyright(c) 2006-2007, Ext JS, LLC.
11173  *
11174  * Originally Released Under LGPL - original licence link has changed is not relivant.
11175  *
11176  * Fork - LGPL
11177  * <script type="text/javascript">
11178  */
11179
11180 /**
11181  * @class Roo.data.ArrayReader
11182  * @extends Roo.data.DataReader
11183  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11184  * Each element of that Array represents a row of data fields. The
11185  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11186  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11187  * <p>
11188  * Example code:.
11189  * <pre><code>
11190 var RecordDef = Roo.data.Record.create([
11191     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11192     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11193 ]);
11194 var myReader = new Roo.data.ArrayReader({
11195     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11196 }, RecordDef);
11197 </code></pre>
11198  * <p>
11199  * This would consume an Array like this:
11200  * <pre><code>
11201 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11202   </code></pre>
11203  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11204  * @constructor
11205  * Create a new JsonReader
11206  * @param {Object} meta Metadata configuration options.
11207  * @param {Object} recordType Either an Array of field definition objects
11208  * as specified to {@link Roo.data.Record#create},
11209  * or an {@link Roo.data.Record} object
11210  * created using {@link Roo.data.Record#create}.
11211  */
11212 Roo.data.ArrayReader = function(meta, recordType){
11213     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11214 };
11215
11216 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11217     /**
11218      * Create a data block containing Roo.data.Records from an XML document.
11219      * @param {Object} o An Array of row objects which represents the dataset.
11220      * @return {Object} data A data block which is used by an Roo.data.Store object as
11221      * a cache of Roo.data.Records.
11222      */
11223     readRecords : function(o){
11224         var sid = this.meta ? this.meta.id : null;
11225         var recordType = this.recordType, fields = recordType.prototype.fields;
11226         var records = [];
11227         var root = o;
11228             for(var i = 0; i < root.length; i++){
11229                     var n = root[i];
11230                 var values = {};
11231                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11232                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11233                 var f = fields.items[j];
11234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11236                 v = f.convert(v);
11237                 values[f.name] = v;
11238             }
11239                 var record = new recordType(values, id);
11240                 record.json = n;
11241                 records[records.length] = record;
11242             }
11243             return {
11244                 records : records,
11245                 totalRecords : records.length
11246             };
11247     }
11248 });/*
11249  * - LGPL
11250  * * 
11251  */
11252
11253 /**
11254  * @class Roo.bootstrap.ComboBox
11255  * @extends Roo.bootstrap.TriggerField
11256  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11257  * @cfg {Boolean} append (true|false) default false
11258  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11259  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11260  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11261  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11262  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11263  * @cfg {Boolean} animate default true
11264  * @cfg {Boolean} emptyResultText only for touch device
11265  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11266  * @constructor
11267  * Create a new ComboBox.
11268  * @param {Object} config Configuration options
11269  */
11270 Roo.bootstrap.ComboBox = function(config){
11271     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11272     this.addEvents({
11273         /**
11274          * @event expand
11275          * Fires when the dropdown list is expanded
11276              * @param {Roo.bootstrap.ComboBox} combo This combo box
11277              */
11278         'expand' : true,
11279         /**
11280          * @event collapse
11281          * Fires when the dropdown list is collapsed
11282              * @param {Roo.bootstrap.ComboBox} combo This combo box
11283              */
11284         'collapse' : true,
11285         /**
11286          * @event beforeselect
11287          * Fires before a list item is selected. Return false to cancel the selection.
11288              * @param {Roo.bootstrap.ComboBox} combo This combo box
11289              * @param {Roo.data.Record} record The data record returned from the underlying store
11290              * @param {Number} index The index of the selected item in the dropdown list
11291              */
11292         'beforeselect' : true,
11293         /**
11294          * @event select
11295          * Fires when a list item is selected
11296              * @param {Roo.bootstrap.ComboBox} combo This combo box
11297              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11298              * @param {Number} index The index of the selected item in the dropdown list
11299              */
11300         'select' : true,
11301         /**
11302          * @event beforequery
11303          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11304          * The event object passed has these properties:
11305              * @param {Roo.bootstrap.ComboBox} combo This combo box
11306              * @param {String} query The query
11307              * @param {Boolean} forceAll true to force "all" query
11308              * @param {Boolean} cancel true to cancel the query
11309              * @param {Object} e The query event object
11310              */
11311         'beforequery': true,
11312          /**
11313          * @event add
11314          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11315              * @param {Roo.bootstrap.ComboBox} combo This combo box
11316              */
11317         'add' : true,
11318         /**
11319          * @event edit
11320          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11321              * @param {Roo.bootstrap.ComboBox} combo This combo box
11322              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11323              */
11324         'edit' : true,
11325         /**
11326          * @event remove
11327          * Fires when the remove value from the combobox array
11328              * @param {Roo.bootstrap.ComboBox} combo This combo box
11329              */
11330         'remove' : true,
11331         /**
11332          * @event specialfilter
11333          * Fires when specialfilter
11334             * @param {Roo.bootstrap.ComboBox} combo This combo box
11335             */
11336         'specialfilter' : true
11337         
11338     });
11339     
11340     this.item = [];
11341     this.tickItems = [];
11342     
11343     this.selectedIndex = -1;
11344     if(this.mode == 'local'){
11345         if(config.queryDelay === undefined){
11346             this.queryDelay = 10;
11347         }
11348         if(config.minChars === undefined){
11349             this.minChars = 0;
11350         }
11351     }
11352 };
11353
11354 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11355      
11356     /**
11357      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11358      * rendering into an Roo.Editor, defaults to false)
11359      */
11360     /**
11361      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11362      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11363      */
11364     /**
11365      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11366      */
11367     /**
11368      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11369      * the dropdown list (defaults to undefined, with no header element)
11370      */
11371
11372      /**
11373      * @cfg {String/Roo.Template} tpl The template to use to render the output
11374      */
11375      
11376      /**
11377      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11378      */
11379     listWidth: undefined,
11380     /**
11381      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11382      * mode = 'remote' or 'text' if mode = 'local')
11383      */
11384     displayField: undefined,
11385     
11386     /**
11387      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11388      * mode = 'remote' or 'value' if mode = 'local'). 
11389      * Note: use of a valueField requires the user make a selection
11390      * in order for a value to be mapped.
11391      */
11392     valueField: undefined,
11393     
11394     
11395     /**
11396      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11397      * field's data value (defaults to the underlying DOM element's name)
11398      */
11399     hiddenName: undefined,
11400     /**
11401      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11402      */
11403     listClass: '',
11404     /**
11405      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11406      */
11407     selectedClass: 'active',
11408     
11409     /**
11410      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11411      */
11412     shadow:'sides',
11413     /**
11414      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11415      * anchor positions (defaults to 'tl-bl')
11416      */
11417     listAlign: 'tl-bl?',
11418     /**
11419      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11420      */
11421     maxHeight: 300,
11422     /**
11423      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11424      * query specified by the allQuery config option (defaults to 'query')
11425      */
11426     triggerAction: 'query',
11427     /**
11428      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11429      * (defaults to 4, does not apply if editable = false)
11430      */
11431     minChars : 4,
11432     /**
11433      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11434      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11435      */
11436     typeAhead: false,
11437     /**
11438      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11439      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11440      */
11441     queryDelay: 500,
11442     /**
11443      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11444      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11445      */
11446     pageSize: 0,
11447     /**
11448      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11449      * when editable = true (defaults to false)
11450      */
11451     selectOnFocus:false,
11452     /**
11453      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11454      */
11455     queryParam: 'query',
11456     /**
11457      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11458      * when mode = 'remote' (defaults to 'Loading...')
11459      */
11460     loadingText: 'Loading...',
11461     /**
11462      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11463      */
11464     resizable: false,
11465     /**
11466      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11467      */
11468     handleHeight : 8,
11469     /**
11470      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11471      * traditional select (defaults to true)
11472      */
11473     editable: true,
11474     /**
11475      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11476      */
11477     allQuery: '',
11478     /**
11479      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11480      */
11481     mode: 'remote',
11482     /**
11483      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11484      * listWidth has a higher value)
11485      */
11486     minListWidth : 70,
11487     /**
11488      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11489      * allow the user to set arbitrary text into the field (defaults to false)
11490      */
11491     forceSelection:false,
11492     /**
11493      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11494      * if typeAhead = true (defaults to 250)
11495      */
11496     typeAheadDelay : 250,
11497     /**
11498      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11499      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11500      */
11501     valueNotFoundText : undefined,
11502     /**
11503      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11504      */
11505     blockFocus : false,
11506     
11507     /**
11508      * @cfg {Boolean} disableClear Disable showing of clear button.
11509      */
11510     disableClear : false,
11511     /**
11512      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11513      */
11514     alwaysQuery : false,
11515     
11516     /**
11517      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11518      */
11519     multiple : false,
11520     
11521     /**
11522      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11523      */
11524     invalidClass : "has-warning",
11525     
11526     /**
11527      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11528      */
11529     validClass : "has-success",
11530     
11531     /**
11532      * @cfg {Boolean} specialFilter (true|false) special filter default false
11533      */
11534     specialFilter : false,
11535     
11536     /**
11537      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11538      */
11539     mobileTouchView : true,
11540     
11541     //private
11542     addicon : false,
11543     editicon: false,
11544     
11545     page: 0,
11546     hasQuery: false,
11547     append: false,
11548     loadNext: false,
11549     autoFocus : true,
11550     tickable : false,
11551     btnPosition : 'right',
11552     triggerList : true,
11553     showToggleBtn : true,
11554     animate : true,
11555     emptyResultText: 'Empty',
11556     triggerText : 'Select',
11557     
11558     // element that contains real text value.. (when hidden is used..)
11559     
11560     getAutoCreate : function()
11561     {
11562         var cfg = false;
11563         
11564         /*
11565          * Touch Devices
11566          */
11567         
11568         if(Roo.isTouch && this.mobileTouchView){
11569             cfg = this.getAutoCreateTouchView();
11570             return cfg;;
11571         }
11572         
11573         /*
11574          *  Normal ComboBox
11575          */
11576         if(!this.tickable){
11577             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11578             return cfg;
11579         }
11580         
11581         /*
11582          *  ComboBox with tickable selections
11583          */
11584              
11585         var align = this.labelAlign || this.parentLabelAlign();
11586         
11587         cfg = {
11588             cls : 'form-group roo-combobox-tickable' //input-group
11589         };
11590         
11591         var buttons = {
11592             tag : 'div',
11593             cls : 'tickable-buttons',
11594             cn : [
11595                 {
11596                     tag : 'button',
11597                     type : 'button',
11598                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11599                     html : this.triggerText
11600                 },
11601                 {
11602                     tag : 'button',
11603                     type : 'button',
11604                     name : 'ok',
11605                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11606                     html : 'Done'
11607                 },
11608                 {
11609                     tag : 'button',
11610                     type : 'button',
11611                     name : 'cancel',
11612                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11613                     html : 'Cancel'
11614                 }
11615             ]
11616         };
11617         
11618         if(this.editable){
11619             buttons.cn.unshift({
11620                 tag: 'input',
11621                 cls: 'select2-search-field-input'
11622             });
11623         }
11624         
11625         var _this = this;
11626         
11627         Roo.each(buttons.cn, function(c){
11628             if (_this.size) {
11629                 c.cls += ' btn-' + _this.size;
11630             }
11631
11632             if (_this.disabled) {
11633                 c.disabled = true;
11634             }
11635         });
11636         
11637         var box = {
11638             tag: 'div',
11639             cn: [
11640                 {
11641                     tag: 'input',
11642                     type : 'hidden',
11643                     cls: 'form-hidden-field'
11644                 },
11645                 {
11646                     tag: 'ul',
11647                     cls: 'select2-choices',
11648                     cn:[
11649                         {
11650                             tag: 'li',
11651                             cls: 'select2-search-field',
11652                             cn: [
11653
11654                                 buttons
11655                             ]
11656                         }
11657                     ]
11658                 }
11659             ]
11660         };
11661         
11662         var combobox = {
11663             cls: 'select2-container input-group select2-container-multi',
11664             cn: [
11665                 box
11666 //                {
11667 //                    tag: 'ul',
11668 //                    cls: 'typeahead typeahead-long dropdown-menu',
11669 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11670 //                }
11671             ]
11672         };
11673         
11674         if(this.hasFeedback && !this.allowBlank){
11675             
11676             var feedback = {
11677                 tag: 'span',
11678                 cls: 'glyphicon form-control-feedback'
11679             };
11680
11681             combobox.cn.push(feedback);
11682         }
11683         
11684         if (align ==='left' && this.fieldLabel.length) {
11685             
11686                 Roo.log("left and has label");
11687                 cfg.cn = [
11688                     
11689                     {
11690                         tag: 'label',
11691                         'for' :  id,
11692                         cls : 'control-label col-sm-' + this.labelWidth,
11693                         html : this.fieldLabel
11694                         
11695                     },
11696                     {
11697                         cls : "col-sm-" + (12 - this.labelWidth), 
11698                         cn: [
11699                             combobox
11700                         ]
11701                     }
11702                     
11703                 ];
11704         } else if ( this.fieldLabel.length) {
11705                 Roo.log(" label");
11706                  cfg.cn = [
11707                    
11708                     {
11709                         tag: 'label',
11710                         //cls : 'input-group-addon',
11711                         html : this.fieldLabel
11712                         
11713                     },
11714                     
11715                     combobox
11716                     
11717                 ];
11718
11719         } else {
11720             
11721                 Roo.log(" no label && no align");
11722                 cfg = combobox
11723                      
11724                 
11725         }
11726          
11727         var settings=this;
11728         ['xs','sm','md','lg'].map(function(size){
11729             if (settings[size]) {
11730                 cfg.cls += ' col-' + size + '-' + settings[size];
11731             }
11732         });
11733         
11734         return cfg;
11735         
11736     },
11737     
11738     _initEventsCalled : false,
11739     
11740     // private
11741     initEvents: function()
11742     {
11743         
11744         if (this._initEventsCalled) { // as we call render... prevent looping...
11745             return;
11746         }
11747         this._initEventsCalled = true;
11748         
11749         if (!this.store) {
11750             throw "can not find store for combo";
11751         }
11752         
11753         this.store = Roo.factory(this.store, Roo.data);
11754         
11755         // if we are building from html. then this element is so complex, that we can not really
11756         // use the rendered HTML.
11757         // so we have to trash and replace the previous code.
11758         if (Roo.XComponent.build_from_html) {
11759             
11760             // remove this element....
11761             var e = this.el.dom, k=0;
11762             while (e ) { e = e.previousSibling;  ++k;}
11763
11764             this.el.remove();
11765             
11766             this.el=false;
11767             this.rendered = false;
11768             
11769             this.render(this.parent().getChildContainer(true), k);
11770             
11771             
11772             
11773         }
11774         
11775         
11776         /*
11777          * Touch Devices
11778          */
11779         
11780         if(Roo.isTouch && this.mobileTouchView){
11781             this.initTouchView();
11782             return;
11783         }
11784         
11785         if(this.tickable){
11786             this.initTickableEvents();
11787             return;
11788         }
11789         
11790         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11791         
11792         if(this.hiddenName){
11793             
11794             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11795             
11796             this.hiddenField.dom.value =
11797                 this.hiddenValue !== undefined ? this.hiddenValue :
11798                 this.value !== undefined ? this.value : '';
11799
11800             // prevent input submission
11801             this.el.dom.removeAttribute('name');
11802             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11803              
11804              
11805         }
11806         //if(Roo.isGecko){
11807         //    this.el.dom.setAttribute('autocomplete', 'off');
11808         //}
11809         
11810         var cls = 'x-combo-list';
11811         
11812         //this.list = new Roo.Layer({
11813         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11814         //});
11815         
11816         var _this = this;
11817         
11818         (function(){
11819             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11820             _this.list.setWidth(lw);
11821         }).defer(100);
11822         
11823         this.list.on('mouseover', this.onViewOver, this);
11824         this.list.on('mousemove', this.onViewMove, this);
11825         
11826         this.list.on('scroll', this.onViewScroll, this);
11827         
11828         /*
11829         this.list.swallowEvent('mousewheel');
11830         this.assetHeight = 0;
11831
11832         if(this.title){
11833             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11834             this.assetHeight += this.header.getHeight();
11835         }
11836
11837         this.innerList = this.list.createChild({cls:cls+'-inner'});
11838         this.innerList.on('mouseover', this.onViewOver, this);
11839         this.innerList.on('mousemove', this.onViewMove, this);
11840         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11841         
11842         if(this.allowBlank && !this.pageSize && !this.disableClear){
11843             this.footer = this.list.createChild({cls:cls+'-ft'});
11844             this.pageTb = new Roo.Toolbar(this.footer);
11845            
11846         }
11847         if(this.pageSize){
11848             this.footer = this.list.createChild({cls:cls+'-ft'});
11849             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11850                     {pageSize: this.pageSize});
11851             
11852         }
11853         
11854         if (this.pageTb && this.allowBlank && !this.disableClear) {
11855             var _this = this;
11856             this.pageTb.add(new Roo.Toolbar.Fill(), {
11857                 cls: 'x-btn-icon x-btn-clear',
11858                 text: '&#160;',
11859                 handler: function()
11860                 {
11861                     _this.collapse();
11862                     _this.clearValue();
11863                     _this.onSelect(false, -1);
11864                 }
11865             });
11866         }
11867         if (this.footer) {
11868             this.assetHeight += this.footer.getHeight();
11869         }
11870         */
11871             
11872         if(!this.tpl){
11873             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11874         }
11875
11876         this.view = new Roo.View(this.list, this.tpl, {
11877             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11878         });
11879         //this.view.wrapEl.setDisplayed(false);
11880         this.view.on('click', this.onViewClick, this);
11881         
11882         
11883         
11884         this.store.on('beforeload', this.onBeforeLoad, this);
11885         this.store.on('load', this.onLoad, this);
11886         this.store.on('loadexception', this.onLoadException, this);
11887         /*
11888         if(this.resizable){
11889             this.resizer = new Roo.Resizable(this.list,  {
11890                pinned:true, handles:'se'
11891             });
11892             this.resizer.on('resize', function(r, w, h){
11893                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11894                 this.listWidth = w;
11895                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11896                 this.restrictHeight();
11897             }, this);
11898             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11899         }
11900         */
11901         if(!this.editable){
11902             this.editable = true;
11903             this.setEditable(false);
11904         }
11905         
11906         /*
11907         
11908         if (typeof(this.events.add.listeners) != 'undefined') {
11909             
11910             this.addicon = this.wrap.createChild(
11911                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11912        
11913             this.addicon.on('click', function(e) {
11914                 this.fireEvent('add', this);
11915             }, this);
11916         }
11917         if (typeof(this.events.edit.listeners) != 'undefined') {
11918             
11919             this.editicon = this.wrap.createChild(
11920                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11921             if (this.addicon) {
11922                 this.editicon.setStyle('margin-left', '40px');
11923             }
11924             this.editicon.on('click', function(e) {
11925                 
11926                 // we fire even  if inothing is selected..
11927                 this.fireEvent('edit', this, this.lastData );
11928                 
11929             }, this);
11930         }
11931         */
11932         
11933         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11934             "up" : function(e){
11935                 this.inKeyMode = true;
11936                 this.selectPrev();
11937             },
11938
11939             "down" : function(e){
11940                 if(!this.isExpanded()){
11941                     this.onTriggerClick();
11942                 }else{
11943                     this.inKeyMode = true;
11944                     this.selectNext();
11945                 }
11946             },
11947
11948             "enter" : function(e){
11949 //                this.onViewClick();
11950                 //return true;
11951                 this.collapse();
11952                 
11953                 if(this.fireEvent("specialkey", this, e)){
11954                     this.onViewClick(false);
11955                 }
11956                 
11957                 return true;
11958             },
11959
11960             "esc" : function(e){
11961                 this.collapse();
11962             },
11963
11964             "tab" : function(e){
11965                 this.collapse();
11966                 
11967                 if(this.fireEvent("specialkey", this, e)){
11968                     this.onViewClick(false);
11969                 }
11970                 
11971                 return true;
11972             },
11973
11974             scope : this,
11975
11976             doRelay : function(foo, bar, hname){
11977                 if(hname == 'down' || this.scope.isExpanded()){
11978                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11979                 }
11980                 return true;
11981             },
11982
11983             forceKeyDown: true
11984         });
11985         
11986         
11987         this.queryDelay = Math.max(this.queryDelay || 10,
11988                 this.mode == 'local' ? 10 : 250);
11989         
11990         
11991         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11992         
11993         if(this.typeAhead){
11994             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11995         }
11996         if(this.editable !== false){
11997             this.inputEl().on("keyup", this.onKeyUp, this);
11998         }
11999         if(this.forceSelection){
12000             this.inputEl().on('blur', this.doForce, this);
12001         }
12002         
12003         if(this.multiple){
12004             this.choices = this.el.select('ul.select2-choices', true).first();
12005             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12006         }
12007     },
12008     
12009     initTickableEvents: function()
12010     {   
12011         this.createList();
12012         
12013         if(this.hiddenName){
12014             
12015             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12016             
12017             this.hiddenField.dom.value =
12018                 this.hiddenValue !== undefined ? this.hiddenValue :
12019                 this.value !== undefined ? this.value : '';
12020
12021             // prevent input submission
12022             this.el.dom.removeAttribute('name');
12023             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12024              
12025              
12026         }
12027         
12028 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12029         
12030         this.choices = this.el.select('ul.select2-choices', true).first();
12031         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12032         if(this.triggerList){
12033             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12034         }
12035          
12036         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12037         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12038         
12039         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12040         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12041         
12042         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12043         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12044         
12045         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12046         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12047         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12048         
12049         this.okBtn.hide();
12050         this.cancelBtn.hide();
12051         
12052         var _this = this;
12053         
12054         (function(){
12055             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12056             _this.list.setWidth(lw);
12057         }).defer(100);
12058         
12059         this.list.on('mouseover', this.onViewOver, this);
12060         this.list.on('mousemove', this.onViewMove, this);
12061         
12062         this.list.on('scroll', this.onViewScroll, this);
12063         
12064         if(!this.tpl){
12065             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>';
12066         }
12067
12068         this.view = new Roo.View(this.list, this.tpl, {
12069             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12070         });
12071         
12072         //this.view.wrapEl.setDisplayed(false);
12073         this.view.on('click', this.onViewClick, this);
12074         
12075         
12076         
12077         this.store.on('beforeload', this.onBeforeLoad, this);
12078         this.store.on('load', this.onLoad, this);
12079         this.store.on('loadexception', this.onLoadException, this);
12080         
12081         if(this.editable){
12082             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12083                 "up" : function(e){
12084                     this.inKeyMode = true;
12085                     this.selectPrev();
12086                 },
12087
12088                 "down" : function(e){
12089                     this.inKeyMode = true;
12090                     this.selectNext();
12091                 },
12092
12093                 "enter" : function(e){
12094                     if(this.fireEvent("specialkey", this, e)){
12095                         this.onViewClick(false);
12096                     }
12097                     
12098                     return true;
12099                 },
12100
12101                 "esc" : function(e){
12102                     this.onTickableFooterButtonClick(e, false, false);
12103                 },
12104
12105                 "tab" : function(e){
12106                     this.fireEvent("specialkey", this, e);
12107                     
12108                     this.onTickableFooterButtonClick(e, false, false);
12109                     
12110                     return true;
12111                 },
12112
12113                 scope : this,
12114
12115                 doRelay : function(e, fn, key){
12116                     if(this.scope.isExpanded()){
12117                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12118                     }
12119                     return true;
12120                 },
12121
12122                 forceKeyDown: true
12123             });
12124         }
12125         
12126         this.queryDelay = Math.max(this.queryDelay || 10,
12127                 this.mode == 'local' ? 10 : 250);
12128         
12129         
12130         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12131         
12132         if(this.typeAhead){
12133             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12134         }
12135         
12136         if(this.editable !== false){
12137             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12138         }
12139         
12140     },
12141
12142     onDestroy : function(){
12143         if(this.view){
12144             this.view.setStore(null);
12145             this.view.el.removeAllListeners();
12146             this.view.el.remove();
12147             this.view.purgeListeners();
12148         }
12149         if(this.list){
12150             this.list.dom.innerHTML  = '';
12151         }
12152         
12153         if(this.store){
12154             this.store.un('beforeload', this.onBeforeLoad, this);
12155             this.store.un('load', this.onLoad, this);
12156             this.store.un('loadexception', this.onLoadException, this);
12157         }
12158         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12159     },
12160
12161     // private
12162     fireKey : function(e){
12163         if(e.isNavKeyPress() && !this.list.isVisible()){
12164             this.fireEvent("specialkey", this, e);
12165         }
12166     },
12167
12168     // private
12169     onResize: function(w, h){
12170 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12171 //        
12172 //        if(typeof w != 'number'){
12173 //            // we do not handle it!?!?
12174 //            return;
12175 //        }
12176 //        var tw = this.trigger.getWidth();
12177 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12178 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12179 //        var x = w - tw;
12180 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12181 //            
12182 //        //this.trigger.setStyle('left', x+'px');
12183 //        
12184 //        if(this.list && this.listWidth === undefined){
12185 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12186 //            this.list.setWidth(lw);
12187 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12188 //        }
12189         
12190     
12191         
12192     },
12193
12194     /**
12195      * Allow or prevent the user from directly editing the field text.  If false is passed,
12196      * the user will only be able to select from the items defined in the dropdown list.  This method
12197      * is the runtime equivalent of setting the 'editable' config option at config time.
12198      * @param {Boolean} value True to allow the user to directly edit the field text
12199      */
12200     setEditable : function(value){
12201         if(value == this.editable){
12202             return;
12203         }
12204         this.editable = value;
12205         if(!value){
12206             this.inputEl().dom.setAttribute('readOnly', true);
12207             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12208             this.inputEl().addClass('x-combo-noedit');
12209         }else{
12210             this.inputEl().dom.setAttribute('readOnly', false);
12211             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12212             this.inputEl().removeClass('x-combo-noedit');
12213         }
12214     },
12215
12216     // private
12217     
12218     onBeforeLoad : function(combo,opts){
12219         if(!this.hasFocus){
12220             return;
12221         }
12222          if (!opts.add) {
12223             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12224          }
12225         this.restrictHeight();
12226         this.selectedIndex = -1;
12227     },
12228
12229     // private
12230     onLoad : function(){
12231         
12232         this.hasQuery = false;
12233         
12234         if(!this.hasFocus){
12235             return;
12236         }
12237         
12238         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12239             this.loading.hide();
12240         }
12241              
12242         if(this.store.getCount() > 0){
12243             this.expand();
12244             this.restrictHeight();
12245             if(this.lastQuery == this.allQuery){
12246                 if(this.editable && !this.tickable){
12247                     this.inputEl().dom.select();
12248                 }
12249                 
12250                 if(
12251                     !this.selectByValue(this.value, true) &&
12252                     this.autoFocus && 
12253                     (
12254                         !this.store.lastOptions ||
12255                         typeof(this.store.lastOptions.add) == 'undefined' || 
12256                         this.store.lastOptions.add != true
12257                     )
12258                 ){
12259                     this.select(0, true);
12260                 }
12261             }else{
12262                 if(this.autoFocus){
12263                     this.selectNext();
12264                 }
12265                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12266                     this.taTask.delay(this.typeAheadDelay);
12267                 }
12268             }
12269         }else{
12270             this.onEmptyResults();
12271         }
12272         
12273         //this.el.focus();
12274     },
12275     // private
12276     onLoadException : function()
12277     {
12278         this.hasQuery = false;
12279         
12280         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12281             this.loading.hide();
12282         }
12283         
12284         if(this.tickable && this.editable){
12285             return;
12286         }
12287         
12288         this.collapse();
12289         
12290         Roo.log(this.store.reader.jsonData);
12291         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12292             // fixme
12293             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12294         }
12295         
12296         
12297     },
12298     // private
12299     onTypeAhead : function(){
12300         if(this.store.getCount() > 0){
12301             var r = this.store.getAt(0);
12302             var newValue = r.data[this.displayField];
12303             var len = newValue.length;
12304             var selStart = this.getRawValue().length;
12305             
12306             if(selStart != len){
12307                 this.setRawValue(newValue);
12308                 this.selectText(selStart, newValue.length);
12309             }
12310         }
12311     },
12312
12313     // private
12314     onSelect : function(record, index){
12315         
12316         if(this.fireEvent('beforeselect', this, record, index) !== false){
12317         
12318             this.setFromData(index > -1 ? record.data : false);
12319             
12320             this.collapse();
12321             this.fireEvent('select', this, record, index);
12322         }
12323     },
12324
12325     /**
12326      * Returns the currently selected field value or empty string if no value is set.
12327      * @return {String} value The selected value
12328      */
12329     getValue : function(){
12330         
12331         if(this.multiple){
12332             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12333         }
12334         
12335         if(this.valueField){
12336             return typeof this.value != 'undefined' ? this.value : '';
12337         }else{
12338             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12339         }
12340     },
12341
12342     /**
12343      * Clears any text/value currently set in the field
12344      */
12345     clearValue : function(){
12346         if(this.hiddenField){
12347             this.hiddenField.dom.value = '';
12348         }
12349         this.value = '';
12350         this.setRawValue('');
12351         this.lastSelectionText = '';
12352         this.lastData = false;
12353         
12354         var close = this.closeTriggerEl();
12355         
12356         if(close){
12357             close.hide();
12358         }
12359         
12360     },
12361
12362     /**
12363      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12364      * will be displayed in the field.  If the value does not match the data value of an existing item,
12365      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12366      * Otherwise the field will be blank (although the value will still be set).
12367      * @param {String} value The value to match
12368      */
12369     setValue : function(v){
12370         if(this.multiple){
12371             this.syncValue();
12372             return;
12373         }
12374         
12375         var text = v;
12376         if(this.valueField){
12377             var r = this.findRecord(this.valueField, v);
12378             if(r){
12379                 text = r.data[this.displayField];
12380             }else if(this.valueNotFoundText !== undefined){
12381                 text = this.valueNotFoundText;
12382             }
12383         }
12384         this.lastSelectionText = text;
12385         if(this.hiddenField){
12386             this.hiddenField.dom.value = v;
12387         }
12388         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12389         this.value = v;
12390         
12391         var close = this.closeTriggerEl();
12392         
12393         if(close){
12394             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12395         }
12396     },
12397     /**
12398      * @property {Object} the last set data for the element
12399      */
12400     
12401     lastData : false,
12402     /**
12403      * Sets the value of the field based on a object which is related to the record format for the store.
12404      * @param {Object} value the value to set as. or false on reset?
12405      */
12406     setFromData : function(o){
12407         
12408         if(this.multiple){
12409             this.addItem(o);
12410             return;
12411         }
12412             
12413         var dv = ''; // display value
12414         var vv = ''; // value value..
12415         this.lastData = o;
12416         if (this.displayField) {
12417             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12418         } else {
12419             // this is an error condition!!!
12420             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12421         }
12422         
12423         if(this.valueField){
12424             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12425         }
12426         
12427         var close = this.closeTriggerEl();
12428         
12429         if(close){
12430             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12431         }
12432         
12433         if(this.hiddenField){
12434             this.hiddenField.dom.value = vv;
12435             
12436             this.lastSelectionText = dv;
12437             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12438             this.value = vv;
12439             return;
12440         }
12441         // no hidden field.. - we store the value in 'value', but still display
12442         // display field!!!!
12443         this.lastSelectionText = dv;
12444         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12445         this.value = vv;
12446         
12447         
12448         
12449     },
12450     // private
12451     reset : function(){
12452         // overridden so that last data is reset..
12453         
12454         if(this.multiple){
12455             this.clearItem();
12456             return;
12457         }
12458         
12459         this.setValue(this.originalValue);
12460         this.clearInvalid();
12461         this.lastData = false;
12462         if (this.view) {
12463             this.view.clearSelections();
12464         }
12465     },
12466     // private
12467     findRecord : function(prop, value){
12468         var record;
12469         if(this.store.getCount() > 0){
12470             this.store.each(function(r){
12471                 if(r.data[prop] == value){
12472                     record = r;
12473                     return false;
12474                 }
12475                 return true;
12476             });
12477         }
12478         return record;
12479     },
12480     
12481     getName: function()
12482     {
12483         // returns hidden if it's set..
12484         if (!this.rendered) {return ''};
12485         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12486         
12487     },
12488     // private
12489     onViewMove : function(e, t){
12490         this.inKeyMode = false;
12491     },
12492
12493     // private
12494     onViewOver : function(e, t){
12495         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12496             return;
12497         }
12498         var item = this.view.findItemFromChild(t);
12499         
12500         if(item){
12501             var index = this.view.indexOf(item);
12502             this.select(index, false);
12503         }
12504     },
12505
12506     // private
12507     onViewClick : function(view, doFocus, el, e)
12508     {
12509         var index = this.view.getSelectedIndexes()[0];
12510         
12511         var r = this.store.getAt(index);
12512         
12513         if(this.tickable){
12514             
12515             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12516                 return;
12517             }
12518             
12519             var rm = false;
12520             var _this = this;
12521             
12522             Roo.each(this.tickItems, function(v,k){
12523                 
12524                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12525                     _this.tickItems.splice(k, 1);
12526                     
12527                     if(typeof(e) == 'undefined' && view == false){
12528                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12529                     }
12530                     
12531                     rm = true;
12532                     return;
12533                 }
12534             });
12535             
12536             if(rm){
12537                 return;
12538             }
12539             
12540             this.tickItems.push(r.data);
12541             
12542             if(typeof(e) == 'undefined' && view == false){
12543                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12544             }
12545                     
12546             return;
12547         }
12548         
12549         if(r){
12550             this.onSelect(r, index);
12551         }
12552         if(doFocus !== false && !this.blockFocus){
12553             this.inputEl().focus();
12554         }
12555     },
12556
12557     // private
12558     restrictHeight : function(){
12559         //this.innerList.dom.style.height = '';
12560         //var inner = this.innerList.dom;
12561         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12562         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12563         //this.list.beginUpdate();
12564         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12565         this.list.alignTo(this.inputEl(), this.listAlign);
12566         this.list.alignTo(this.inputEl(), this.listAlign);
12567         //this.list.endUpdate();
12568     },
12569
12570     // private
12571     onEmptyResults : function(){
12572         
12573         if(this.tickable && this.editable){
12574             this.restrictHeight();
12575             return;
12576         }
12577         
12578         this.collapse();
12579     },
12580
12581     /**
12582      * Returns true if the dropdown list is expanded, else false.
12583      */
12584     isExpanded : function(){
12585         return this.list.isVisible();
12586     },
12587
12588     /**
12589      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12591      * @param {String} value The data value of the item to select
12592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12593      * selected item if it is not currently in view (defaults to true)
12594      * @return {Boolean} True if the value matched an item in the list, else false
12595      */
12596     selectByValue : function(v, scrollIntoView){
12597         if(v !== undefined && v !== null){
12598             var r = this.findRecord(this.valueField || this.displayField, v);
12599             if(r){
12600                 this.select(this.store.indexOf(r), scrollIntoView);
12601                 return true;
12602             }
12603         }
12604         return false;
12605     },
12606
12607     /**
12608      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12610      * @param {Number} index The zero-based index of the list item to select
12611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12612      * selected item if it is not currently in view (defaults to true)
12613      */
12614     select : function(index, scrollIntoView){
12615         this.selectedIndex = index;
12616         this.view.select(index);
12617         if(scrollIntoView !== false){
12618             var el = this.view.getNode(index);
12619             /*
12620              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12621              */
12622             if(el){
12623                 this.list.scrollChildIntoView(el, false);
12624             }
12625         }
12626     },
12627
12628     // private
12629     selectNext : function(){
12630         var ct = this.store.getCount();
12631         if(ct > 0){
12632             if(this.selectedIndex == -1){
12633                 this.select(0);
12634             }else if(this.selectedIndex < ct-1){
12635                 this.select(this.selectedIndex+1);
12636             }
12637         }
12638     },
12639
12640     // private
12641     selectPrev : function(){
12642         var ct = this.store.getCount();
12643         if(ct > 0){
12644             if(this.selectedIndex == -1){
12645                 this.select(0);
12646             }else if(this.selectedIndex != 0){
12647                 this.select(this.selectedIndex-1);
12648             }
12649         }
12650     },
12651
12652     // private
12653     onKeyUp : function(e){
12654         if(this.editable !== false && !e.isSpecialKey()){
12655             this.lastKey = e.getKey();
12656             this.dqTask.delay(this.queryDelay);
12657         }
12658     },
12659
12660     // private
12661     validateBlur : function(){
12662         return !this.list || !this.list.isVisible();   
12663     },
12664
12665     // private
12666     initQuery : function(){
12667         
12668         var v = this.getRawValue();
12669         
12670         if(this.tickable && this.editable){
12671             v = this.tickableInputEl().getValue();
12672         }
12673         
12674         this.doQuery(v);
12675     },
12676
12677     // private
12678     doForce : function(){
12679         if(this.inputEl().dom.value.length > 0){
12680             this.inputEl().dom.value =
12681                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12682              
12683         }
12684     },
12685
12686     /**
12687      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12688      * query allowing the query action to be canceled if needed.
12689      * @param {String} query The SQL query to execute
12690      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12691      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12692      * saved in the current store (defaults to false)
12693      */
12694     doQuery : function(q, forceAll){
12695         
12696         if(q === undefined || q === null){
12697             q = '';
12698         }
12699         var qe = {
12700             query: q,
12701             forceAll: forceAll,
12702             combo: this,
12703             cancel:false
12704         };
12705         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12706             return false;
12707         }
12708         q = qe.query;
12709         
12710         forceAll = qe.forceAll;
12711         if(forceAll === true || (q.length >= this.minChars)){
12712             
12713             this.hasQuery = true;
12714             
12715             if(this.lastQuery != q || this.alwaysQuery){
12716                 this.lastQuery = q;
12717                 if(this.mode == 'local'){
12718                     this.selectedIndex = -1;
12719                     if(forceAll){
12720                         this.store.clearFilter();
12721                     }else{
12722                         
12723                         if(this.specialFilter){
12724                             this.fireEvent('specialfilter', this);
12725                             this.onLoad();
12726                             return;
12727                         }
12728                         
12729                         this.store.filter(this.displayField, q);
12730                     }
12731                     
12732                     this.store.fireEvent("datachanged", this.store);
12733                     
12734                     this.onLoad();
12735                     
12736                     
12737                 }else{
12738                     
12739                     this.store.baseParams[this.queryParam] = q;
12740                     
12741                     var options = {params : this.getParams(q)};
12742                     
12743                     if(this.loadNext){
12744                         options.add = true;
12745                         options.params.start = this.page * this.pageSize;
12746                     }
12747                     
12748                     this.store.load(options);
12749                     
12750                     /*
12751                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12752                      *  we should expand the list on onLoad
12753                      *  so command out it
12754                      */
12755 //                    this.expand();
12756                 }
12757             }else{
12758                 this.selectedIndex = -1;
12759                 this.onLoad();   
12760             }
12761         }
12762         
12763         this.loadNext = false;
12764     },
12765     
12766     // private
12767     getParams : function(q){
12768         var p = {};
12769         //p[this.queryParam] = q;
12770         
12771         if(this.pageSize){
12772             p.start = 0;
12773             p.limit = this.pageSize;
12774         }
12775         return p;
12776     },
12777
12778     /**
12779      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12780      */
12781     collapse : function(){
12782         if(!this.isExpanded()){
12783             return;
12784         }
12785         
12786         this.list.hide();
12787         
12788         if(this.tickable){
12789             this.hasFocus = false;
12790             this.okBtn.hide();
12791             this.cancelBtn.hide();
12792             this.trigger.show();
12793             
12794             if(this.editable){
12795                 this.tickableInputEl().dom.value = '';
12796                 this.tickableInputEl().blur();
12797             }
12798             
12799         }
12800         
12801         Roo.get(document).un('mousedown', this.collapseIf, this);
12802         Roo.get(document).un('mousewheel', this.collapseIf, this);
12803         if (!this.editable) {
12804             Roo.get(document).un('keydown', this.listKeyPress, this);
12805         }
12806         this.fireEvent('collapse', this);
12807     },
12808
12809     // private
12810     collapseIf : function(e){
12811         var in_combo  = e.within(this.el);
12812         var in_list =  e.within(this.list);
12813         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12814         
12815         if (in_combo || in_list || is_list) {
12816             //e.stopPropagation();
12817             return;
12818         }
12819         
12820         if(this.tickable){
12821             this.onTickableFooterButtonClick(e, false, false);
12822         }
12823
12824         this.collapse();
12825         
12826     },
12827
12828     /**
12829      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12830      */
12831     expand : function(){
12832        
12833         if(this.isExpanded() || !this.hasFocus){
12834             return;
12835         }
12836         
12837         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12838         this.list.setWidth(lw);
12839         
12840         
12841          Roo.log('expand');
12842         
12843         this.list.show();
12844         
12845         this.restrictHeight();
12846         
12847         if(this.tickable){
12848             
12849             this.tickItems = Roo.apply([], this.item);
12850             
12851             this.okBtn.show();
12852             this.cancelBtn.show();
12853             this.trigger.hide();
12854             
12855             if(this.editable){
12856                 this.tickableInputEl().focus();
12857             }
12858             
12859         }
12860         
12861         Roo.get(document).on('mousedown', this.collapseIf, this);
12862         Roo.get(document).on('mousewheel', this.collapseIf, this);
12863         if (!this.editable) {
12864             Roo.get(document).on('keydown', this.listKeyPress, this);
12865         }
12866         
12867         this.fireEvent('expand', this);
12868     },
12869
12870     // private
12871     // Implements the default empty TriggerField.onTriggerClick function
12872     onTriggerClick : function(e)
12873     {
12874         Roo.log('trigger click');
12875         
12876         if(this.disabled || !this.triggerList){
12877             return;
12878         }
12879         
12880         this.page = 0;
12881         this.loadNext = false;
12882         
12883         if(this.isExpanded()){
12884             this.collapse();
12885             if (!this.blockFocus) {
12886                 this.inputEl().focus();
12887             }
12888             
12889         }else {
12890             this.hasFocus = true;
12891             if(this.triggerAction == 'all') {
12892                 this.doQuery(this.allQuery, true);
12893             } else {
12894                 this.doQuery(this.getRawValue());
12895             }
12896             if (!this.blockFocus) {
12897                 this.inputEl().focus();
12898             }
12899         }
12900     },
12901     
12902     onTickableTriggerClick : function(e)
12903     {
12904         if(this.disabled){
12905             return;
12906         }
12907         
12908         this.page = 0;
12909         this.loadNext = false;
12910         this.hasFocus = true;
12911         
12912         if(this.triggerAction == 'all') {
12913             this.doQuery(this.allQuery, true);
12914         } else {
12915             this.doQuery(this.getRawValue());
12916         }
12917     },
12918     
12919     onSearchFieldClick : function(e)
12920     {
12921         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12922             this.onTickableFooterButtonClick(e, false, false);
12923             return;
12924         }
12925         
12926         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12927             return;
12928         }
12929         
12930         this.page = 0;
12931         this.loadNext = false;
12932         this.hasFocus = true;
12933         
12934         if(this.triggerAction == 'all') {
12935             this.doQuery(this.allQuery, true);
12936         } else {
12937             this.doQuery(this.getRawValue());
12938         }
12939     },
12940     
12941     listKeyPress : function(e)
12942     {
12943         //Roo.log('listkeypress');
12944         // scroll to first matching element based on key pres..
12945         if (e.isSpecialKey()) {
12946             return false;
12947         }
12948         var k = String.fromCharCode(e.getKey()).toUpperCase();
12949         //Roo.log(k);
12950         var match  = false;
12951         var csel = this.view.getSelectedNodes();
12952         var cselitem = false;
12953         if (csel.length) {
12954             var ix = this.view.indexOf(csel[0]);
12955             cselitem  = this.store.getAt(ix);
12956             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12957                 cselitem = false;
12958             }
12959             
12960         }
12961         
12962         this.store.each(function(v) { 
12963             if (cselitem) {
12964                 // start at existing selection.
12965                 if (cselitem.id == v.id) {
12966                     cselitem = false;
12967                 }
12968                 return true;
12969             }
12970                 
12971             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12972                 match = this.store.indexOf(v);
12973                 return false;
12974             }
12975             return true;
12976         }, this);
12977         
12978         if (match === false) {
12979             return true; // no more action?
12980         }
12981         // scroll to?
12982         this.view.select(match);
12983         var sn = Roo.get(this.view.getSelectedNodes()[0])
12984         sn.scrollIntoView(sn.dom.parentNode, false);
12985     },
12986     
12987     onViewScroll : function(e, t){
12988         
12989         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){
12990             return;
12991         }
12992         
12993         this.hasQuery = true;
12994         
12995         this.loading = this.list.select('.loading', true).first();
12996         
12997         if(this.loading === null){
12998             this.list.createChild({
12999                 tag: 'div',
13000                 cls: 'loading select2-more-results select2-active',
13001                 html: 'Loading more results...'
13002             })
13003             
13004             this.loading = this.list.select('.loading', true).first();
13005             
13006             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13007             
13008             this.loading.hide();
13009         }
13010         
13011         this.loading.show();
13012         
13013         var _combo = this;
13014         
13015         this.page++;
13016         this.loadNext = true;
13017         
13018         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13019         
13020         return;
13021     },
13022     
13023     addItem : function(o)
13024     {   
13025         var dv = ''; // display value
13026         
13027         if (this.displayField) {
13028             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13029         } else {
13030             // this is an error condition!!!
13031             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13032         }
13033         
13034         if(!dv.length){
13035             return;
13036         }
13037         
13038         var choice = this.choices.createChild({
13039             tag: 'li',
13040             cls: 'select2-search-choice',
13041             cn: [
13042                 {
13043                     tag: 'div',
13044                     html: dv
13045                 },
13046                 {
13047                     tag: 'a',
13048                     href: '#',
13049                     cls: 'select2-search-choice-close',
13050                     tabindex: '-1'
13051                 }
13052             ]
13053             
13054         }, this.searchField);
13055         
13056         var close = choice.select('a.select2-search-choice-close', true).first()
13057         
13058         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13059         
13060         this.item.push(o);
13061         
13062         this.lastData = o;
13063         
13064         this.syncValue();
13065         
13066         this.inputEl().dom.value = '';
13067         
13068         this.validate();
13069     },
13070     
13071     onRemoveItem : function(e, _self, o)
13072     {
13073         e.preventDefault();
13074         
13075         this.lastItem = Roo.apply([], this.item);
13076         
13077         var index = this.item.indexOf(o.data) * 1;
13078         
13079         if( index < 0){
13080             Roo.log('not this item?!');
13081             return;
13082         }
13083         
13084         this.item.splice(index, 1);
13085         o.item.remove();
13086         
13087         this.syncValue();
13088         
13089         this.fireEvent('remove', this, e);
13090         
13091         this.validate();
13092         
13093     },
13094     
13095     syncValue : function()
13096     {
13097         if(!this.item.length){
13098             this.clearValue();
13099             return;
13100         }
13101             
13102         var value = [];
13103         var _this = this;
13104         Roo.each(this.item, function(i){
13105             if(_this.valueField){
13106                 value.push(i[_this.valueField]);
13107                 return;
13108             }
13109
13110             value.push(i);
13111         });
13112
13113         this.value = value.join(',');
13114
13115         if(this.hiddenField){
13116             this.hiddenField.dom.value = this.value;
13117         }
13118         
13119         this.store.fireEvent("datachanged", this.store);
13120     },
13121     
13122     clearItem : function()
13123     {
13124         if(!this.multiple){
13125             return;
13126         }
13127         
13128         this.item = [];
13129         
13130         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13131            c.remove();
13132         });
13133         
13134         this.syncValue();
13135         
13136         this.validate();
13137         
13138         if(this.tickable && !Roo.isTouch){
13139             this.view.refresh();
13140         }
13141     },
13142     
13143     inputEl: function ()
13144     {
13145         if(Roo.isTouch && this.mobileTouchView){
13146             return this.el.select('input.form-control',true).first();
13147         }
13148         
13149         if(this.tickable){
13150             return this.searchField;
13151         }
13152         
13153         return this.el.select('input.form-control',true).first();
13154     },
13155     
13156     
13157     onTickableFooterButtonClick : function(e, btn, el)
13158     {
13159         e.preventDefault();
13160         
13161         this.lastItem = Roo.apply([], this.item);
13162         
13163         if(btn && btn.name == 'cancel'){
13164             this.tickItems = Roo.apply([], this.item);
13165             this.collapse();
13166             return;
13167         }
13168         
13169         this.clearItem();
13170         
13171         var _this = this;
13172         
13173         Roo.each(this.tickItems, function(o){
13174             _this.addItem(o);
13175         });
13176         
13177         this.collapse();
13178         
13179     },
13180     
13181     validate : function()
13182     {
13183         var v = this.getRawValue();
13184         
13185         if(this.multiple){
13186             v = this.getValue();
13187         }
13188         
13189         if(this.disabled || this.allowBlank || v.length){
13190             this.markValid();
13191             return true;
13192         }
13193         
13194         this.markInvalid();
13195         return false;
13196     },
13197     
13198     tickableInputEl : function()
13199     {
13200         if(!this.tickable || !this.editable){
13201             return this.inputEl();
13202         }
13203         
13204         return this.inputEl().select('.select2-search-field-input', true).first();
13205     },
13206     
13207     
13208     getAutoCreateTouchView : function()
13209     {
13210         var id = Roo.id();
13211         
13212         var cfg = {
13213             cls: 'form-group' //input-group
13214         };
13215         
13216         var input =  {
13217             tag: 'input',
13218             id : id,
13219             type : this.inputType,
13220             cls : 'form-control x-combo-noedit',
13221             autocomplete: 'new-password',
13222             placeholder : this.placeholder || '',
13223             readonly : true
13224         };
13225         
13226         if (this.name) {
13227             input.name = this.name;
13228         }
13229         
13230         if (this.size) {
13231             input.cls += ' input-' + this.size;
13232         }
13233         
13234         if (this.disabled) {
13235             input.disabled = true;
13236         }
13237         
13238         var inputblock = {
13239             cls : '',
13240             cn : [
13241                 input
13242             ]
13243         };
13244         
13245         if(this.before){
13246             inputblock.cls += ' input-group';
13247             
13248             inputblock.cn.unshift({
13249                 tag :'span',
13250                 cls : 'input-group-addon',
13251                 html : this.before
13252             });
13253         }
13254         
13255         if(this.removable && !this.multiple){
13256             inputblock.cls += ' roo-removable';
13257             
13258             inputblock.cn.push({
13259                 tag: 'button',
13260                 html : 'x',
13261                 cls : 'roo-combo-removable-btn close'
13262             });
13263         }
13264
13265         if(this.hasFeedback && !this.allowBlank){
13266             
13267             inputblock.cls += ' has-feedback';
13268             
13269             inputblock.cn.push({
13270                 tag: 'span',
13271                 cls: 'glyphicon form-control-feedback'
13272             });
13273             
13274         }
13275         
13276         if (this.after) {
13277             
13278             inputblock.cls += (this.before) ? '' : ' input-group';
13279             
13280             inputblock.cn.push({
13281                 tag :'span',
13282                 cls : 'input-group-addon',
13283                 html : this.after
13284             });
13285         }
13286
13287         var box = {
13288             tag: 'div',
13289             cn: [
13290                 {
13291                     tag: 'input',
13292                     type : 'hidden',
13293                     cls: 'form-hidden-field'
13294                 },
13295                 inputblock
13296             ]
13297             
13298         };
13299         
13300         if(this.multiple){
13301             box = {
13302                 tag: 'div',
13303                 cn: [
13304                     {
13305                         tag: 'input',
13306                         type : 'hidden',
13307                         cls: 'form-hidden-field'
13308                     },
13309                     {
13310                         tag: 'ul',
13311                         cls: 'select2-choices',
13312                         cn:[
13313                             {
13314                                 tag: 'li',
13315                                 cls: 'select2-search-field',
13316                                 cn: [
13317
13318                                     inputblock
13319                                 ]
13320                             }
13321                         ]
13322                     }
13323                 ]
13324             }
13325         };
13326         
13327         var combobox = {
13328             cls: 'select2-container input-group',
13329             cn: [
13330                 box
13331             ]
13332         };
13333         
13334         if(this.multiple){
13335             combobox.cls += ' select2-container-multi';
13336         }
13337         
13338         var align = this.labelAlign || this.parentLabelAlign();
13339         
13340         cfg.cn = combobox;
13341         
13342         if(this.fieldLabel.length){
13343             
13344             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13345             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13346             
13347             cfg.cn = [
13348                 {
13349                     tag: 'label',
13350                     cls : 'control-label ' + lw,
13351                     html : this.fieldLabel
13352
13353                 },
13354                 {
13355                     cls : cw, 
13356                     cn: [
13357                         combobox
13358                     ]
13359                 }
13360             ];
13361         }
13362         
13363         var settings = this;
13364         
13365         ['xs','sm','md','lg'].map(function(size){
13366             if (settings[size]) {
13367                 cfg.cls += ' col-' + size + '-' + settings[size];
13368             }
13369         });
13370         
13371         return cfg;
13372     },
13373     
13374     initTouchView : function()
13375     {
13376         this.renderTouchView();
13377         
13378         this.touchViewEl.on('scroll', function(){
13379             this.el.dom.scrollTop = 0;
13380         }, this);
13381         
13382         this.inputEl().on("click", this.showTouchView, this);
13383         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13384         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13385         
13386         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13387         
13388         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13389         this.store.on('load', this.onTouchViewLoad, this);
13390         this.store.on('loadexception', this.onTouchViewLoadException, this);
13391         
13392         if(this.hiddenName){
13393             
13394             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13395             
13396             this.hiddenField.dom.value =
13397                 this.hiddenValue !== undefined ? this.hiddenValue :
13398                 this.value !== undefined ? this.value : '';
13399         
13400             this.el.dom.removeAttribute('name');
13401             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13402         }
13403         
13404         if(this.multiple){
13405             this.choices = this.el.select('ul.select2-choices', true).first();
13406             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13407         }
13408         
13409         if(this.removable && !this.multiple){
13410             var close = this.closeTriggerEl();
13411             if(close){
13412                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13413                 close.on('click', this.removeBtnClick, this, close);
13414             }
13415         }
13416         
13417         return;
13418         
13419         
13420     },
13421     
13422     renderTouchView : function()
13423     {
13424         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13425         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13426         
13427         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13428         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13429         
13430         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13431         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13432         this.touchViewBodyEl.setStyle('overflow', 'auto');
13433         
13434         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13435         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13436         
13437         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13438         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13439         
13440     },
13441     
13442     showTouchView : function()
13443     {
13444         this.touchViewHeaderEl.hide();
13445
13446         if(this.fieldLabel.length){
13447             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13448             this.touchViewHeaderEl.show();
13449         }
13450
13451         this.touchViewEl.show();
13452
13453         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13454         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13455
13456         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13457
13458         if(this.fieldLabel.length){
13459             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13460         }
13461         
13462         this.touchViewBodyEl.setHeight(bodyHeight);
13463
13464         if(this.animate){
13465             var _this = this;
13466             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13467         }else{
13468             this.touchViewEl.addClass('in');
13469         }
13470
13471         this.doTouchViewQuery();
13472         
13473     },
13474     
13475     hideTouchView : function()
13476     {
13477         this.touchViewEl.removeClass('in');
13478
13479         if(this.animate){
13480             var _this = this;
13481             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13482         }else{
13483             this.touchViewEl.setStyle('display', 'none');
13484         }
13485         
13486     },
13487     
13488     setTouchViewValue : function()
13489     {
13490         if(this.multiple){
13491             this.clearItem();
13492         
13493             var _this = this;
13494
13495             Roo.each(this.tickItems, function(o){
13496                 this.addItem(o);
13497             }, this);
13498         }
13499         
13500         this.hideTouchView();
13501     },
13502     
13503     doTouchViewQuery : function()
13504     {
13505         var qe = {
13506             query: '',
13507             forceAll: true,
13508             combo: this,
13509             cancel:false
13510         };
13511         
13512         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13513             return false;
13514         }
13515         
13516         if(!this.alwaysQuery || this.mode == 'local'){
13517             this.onTouchViewLoad();
13518             return;
13519         }
13520         
13521         this.store.load();
13522     },
13523     
13524     onTouchViewBeforeLoad : function(combo,opts)
13525     {
13526         return;
13527     },
13528
13529     // private
13530     onTouchViewLoad : function()
13531     {
13532         if(this.store.getCount() < 1){
13533             this.onTouchViewEmptyResults();
13534             return;
13535         }
13536         
13537         this.clearTouchView();
13538         
13539         var rawValue = this.getRawValue();
13540         
13541         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13542         
13543         this.tickItems = [];
13544         
13545         this.store.data.each(function(d, rowIndex){
13546             var row = this.touchViewListGroup.createChild(template);
13547             
13548             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13549                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13550             }
13551             
13552             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13553                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13554             }
13555             
13556             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13557                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13558                 this.tickItems.push(d.data);
13559             }
13560             
13561             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13562             
13563         }, this);
13564         
13565         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13566         
13567         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13568
13569         if(this.fieldLabel.length){
13570             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13571         }
13572
13573         var listHeight = this.touchViewListGroup.getHeight();
13574         
13575         var _this = this;
13576         
13577         if(firstChecked && listHeight > bodyHeight){
13578             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13579         }
13580         
13581     },
13582     
13583     onTouchViewLoadException : function()
13584     {
13585         this.hideTouchView();
13586     },
13587     
13588     onTouchViewEmptyResults : function()
13589     {
13590         this.clearTouchView();
13591         
13592         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13593         
13594         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13595         
13596     },
13597     
13598     clearTouchView : function()
13599     {
13600         this.touchViewListGroup.dom.innerHTML = '';
13601     },
13602     
13603     onTouchViewClick : function(e, el, o)
13604     {
13605         e.preventDefault();
13606         
13607         var row = o.row;
13608         var rowIndex = o.rowIndex;
13609         
13610         var r = this.store.getAt(rowIndex);
13611         
13612         if(!this.multiple){
13613             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13614                 c.dom.removeAttribute('checked');
13615             }, this);
13616             
13617             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13618         
13619             this.setFromData(r.data);
13620             
13621             var close = this.closeTriggerEl();
13622         
13623             if(close){
13624                 close.show();
13625             }
13626
13627             this.hideTouchView();
13628             
13629             this.fireEvent('select', this, r, rowIndex);
13630             
13631             return;
13632         }
13633         
13634         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13635             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13636             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13637             return;
13638         }
13639         
13640         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13641         this.addItem(r.data);
13642         this.tickItems.push(r.data);
13643         
13644     }
13645     
13646
13647     /** 
13648     * @cfg {Boolean} grow 
13649     * @hide 
13650     */
13651     /** 
13652     * @cfg {Number} growMin 
13653     * @hide 
13654     */
13655     /** 
13656     * @cfg {Number} growMax 
13657     * @hide 
13658     */
13659     /**
13660      * @hide
13661      * @method autoSize
13662      */
13663 });
13664
13665 Roo.apply(Roo.bootstrap.ComboBox,  {
13666     
13667     header : {
13668         tag: 'div',
13669         cls: 'modal-header',
13670         cn: [
13671             {
13672                 tag: 'h4',
13673                 cls: 'modal-title'
13674             }
13675         ]
13676     },
13677     
13678     body : {
13679         tag: 'div',
13680         cls: 'modal-body',
13681         cn: [
13682             {
13683                 tag: 'ul',
13684                 cls: 'list-group'
13685             }
13686         ]
13687     },
13688     
13689     listItemRadio : {
13690         tag: 'li',
13691         cls: 'list-group-item',
13692         cn: [
13693             {
13694                 tag: 'span',
13695                 cls: 'roo-combobox-list-group-item-value'
13696             },
13697             {
13698                 tag: 'div',
13699                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13700                 cn: [
13701                     {
13702                         tag: 'input',
13703                         type: 'radio'
13704                     },
13705                     {
13706                         tag: 'label'
13707                     }
13708                 ]
13709             }
13710         ]
13711     },
13712     
13713     listItemCheckbox : {
13714         tag: 'li',
13715         cls: 'list-group-item',
13716         cn: [
13717             {
13718                 tag: 'span',
13719                 cls: 'roo-combobox-list-group-item-value'
13720             },
13721             {
13722                 tag: 'div',
13723                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13724                 cn: [
13725                     {
13726                         tag: 'input',
13727                         type: 'checkbox'
13728                     },
13729                     {
13730                         tag: 'label'
13731                     }
13732                 ]
13733             }
13734         ]
13735     },
13736     
13737     emptyResult : {
13738         tag: 'div',
13739         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13740     },
13741     
13742     footer : {
13743         tag: 'div',
13744         cls: 'modal-footer',
13745         cn: [
13746             {
13747                 tag: 'div',
13748                 cls: 'row',
13749                 cn: [
13750                     {
13751                         tag: 'div',
13752                         cls: 'col-xs-6 text-left',
13753                         cn: {
13754                             tag: 'button',
13755                             cls: 'btn btn-danger roo-touch-view-cancel',
13756                             html: 'Cancel'
13757                         }
13758                     },
13759                     {
13760                         tag: 'div',
13761                         cls: 'col-xs-6 text-right',
13762                         cn: {
13763                             tag: 'button',
13764                             cls: 'btn btn-success roo-touch-view-ok',
13765                             html: 'OK'
13766                         }
13767                     }
13768                 ]
13769             }
13770         ]
13771         
13772     }
13773 });
13774
13775 Roo.apply(Roo.bootstrap.ComboBox,  {
13776     
13777     touchViewTemplate : {
13778         tag: 'div',
13779         cls: 'modal fade roo-combobox-touch-view',
13780         cn: [
13781             {
13782                 tag: 'div',
13783                 cls: 'modal-dialog',
13784                 cn: [
13785                     {
13786                         tag: 'div',
13787                         cls: 'modal-content',
13788                         cn: [
13789                             Roo.bootstrap.ComboBox.header,
13790                             Roo.bootstrap.ComboBox.body,
13791                             Roo.bootstrap.ComboBox.footer
13792                         ]
13793                     }
13794                 ]
13795             }
13796         ]
13797     }
13798 });/*
13799  * Based on:
13800  * Ext JS Library 1.1.1
13801  * Copyright(c) 2006-2007, Ext JS, LLC.
13802  *
13803  * Originally Released Under LGPL - original licence link has changed is not relivant.
13804  *
13805  * Fork - LGPL
13806  * <script type="text/javascript">
13807  */
13808
13809 /**
13810  * @class Roo.View
13811  * @extends Roo.util.Observable
13812  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13813  * This class also supports single and multi selection modes. <br>
13814  * Create a data model bound view:
13815  <pre><code>
13816  var store = new Roo.data.Store(...);
13817
13818  var view = new Roo.View({
13819     el : "my-element",
13820     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13821  
13822     singleSelect: true,
13823     selectedClass: "ydataview-selected",
13824     store: store
13825  });
13826
13827  // listen for node click?
13828  view.on("click", function(vw, index, node, e){
13829  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13830  });
13831
13832  // load XML data
13833  dataModel.load("foobar.xml");
13834  </code></pre>
13835  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13836  * <br><br>
13837  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13838  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13839  * 
13840  * Note: old style constructor is still suported (container, template, config)
13841  * 
13842  * @constructor
13843  * Create a new View
13844  * @param {Object} config The config object
13845  * 
13846  */
13847 Roo.View = function(config, depreciated_tpl, depreciated_config){
13848     
13849     this.parent = false;
13850     
13851     if (typeof(depreciated_tpl) == 'undefined') {
13852         // new way.. - universal constructor.
13853         Roo.apply(this, config);
13854         this.el  = Roo.get(this.el);
13855     } else {
13856         // old format..
13857         this.el  = Roo.get(config);
13858         this.tpl = depreciated_tpl;
13859         Roo.apply(this, depreciated_config);
13860     }
13861     this.wrapEl  = this.el.wrap().wrap();
13862     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13863     
13864     
13865     if(typeof(this.tpl) == "string"){
13866         this.tpl = new Roo.Template(this.tpl);
13867     } else {
13868         // support xtype ctors..
13869         this.tpl = new Roo.factory(this.tpl, Roo);
13870     }
13871     
13872     
13873     this.tpl.compile();
13874     
13875     /** @private */
13876     this.addEvents({
13877         /**
13878          * @event beforeclick
13879          * Fires before a click is processed. Returns false to cancel the default action.
13880          * @param {Roo.View} this
13881          * @param {Number} index The index of the target node
13882          * @param {HTMLElement} node The target node
13883          * @param {Roo.EventObject} e The raw event object
13884          */
13885             "beforeclick" : true,
13886         /**
13887          * @event click
13888          * Fires when a template node is clicked.
13889          * @param {Roo.View} this
13890          * @param {Number} index The index of the target node
13891          * @param {HTMLElement} node The target node
13892          * @param {Roo.EventObject} e The raw event object
13893          */
13894             "click" : true,
13895         /**
13896          * @event dblclick
13897          * Fires when a template node is double clicked.
13898          * @param {Roo.View} this
13899          * @param {Number} index The index of the target node
13900          * @param {HTMLElement} node The target node
13901          * @param {Roo.EventObject} e The raw event object
13902          */
13903             "dblclick" : true,
13904         /**
13905          * @event contextmenu
13906          * Fires when a template node is right clicked.
13907          * @param {Roo.View} this
13908          * @param {Number} index The index of the target node
13909          * @param {HTMLElement} node The target node
13910          * @param {Roo.EventObject} e The raw event object
13911          */
13912             "contextmenu" : true,
13913         /**
13914          * @event selectionchange
13915          * Fires when the selected nodes change.
13916          * @param {Roo.View} this
13917          * @param {Array} selections Array of the selected nodes
13918          */
13919             "selectionchange" : true,
13920     
13921         /**
13922          * @event beforeselect
13923          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13924          * @param {Roo.View} this
13925          * @param {HTMLElement} node The node to be selected
13926          * @param {Array} selections Array of currently selected nodes
13927          */
13928             "beforeselect" : true,
13929         /**
13930          * @event preparedata
13931          * Fires on every row to render, to allow you to change the data.
13932          * @param {Roo.View} this
13933          * @param {Object} data to be rendered (change this)
13934          */
13935           "preparedata" : true
13936           
13937           
13938         });
13939
13940
13941
13942     this.el.on({
13943         "click": this.onClick,
13944         "dblclick": this.onDblClick,
13945         "contextmenu": this.onContextMenu,
13946         scope:this
13947     });
13948
13949     this.selections = [];
13950     this.nodes = [];
13951     this.cmp = new Roo.CompositeElementLite([]);
13952     if(this.store){
13953         this.store = Roo.factory(this.store, Roo.data);
13954         this.setStore(this.store, true);
13955     }
13956     
13957     if ( this.footer && this.footer.xtype) {
13958            
13959          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13960         
13961         this.footer.dataSource = this.store;
13962         this.footer.container = fctr;
13963         this.footer = Roo.factory(this.footer, Roo);
13964         fctr.insertFirst(this.el);
13965         
13966         // this is a bit insane - as the paging toolbar seems to detach the el..
13967 //        dom.parentNode.parentNode.parentNode
13968          // they get detached?
13969     }
13970     
13971     
13972     Roo.View.superclass.constructor.call(this);
13973     
13974     
13975 };
13976
13977 Roo.extend(Roo.View, Roo.util.Observable, {
13978     
13979      /**
13980      * @cfg {Roo.data.Store} store Data store to load data from.
13981      */
13982     store : false,
13983     
13984     /**
13985      * @cfg {String|Roo.Element} el The container element.
13986      */
13987     el : '',
13988     
13989     /**
13990      * @cfg {String|Roo.Template} tpl The template used by this View 
13991      */
13992     tpl : false,
13993     /**
13994      * @cfg {String} dataName the named area of the template to use as the data area
13995      *                          Works with domtemplates roo-name="name"
13996      */
13997     dataName: false,
13998     /**
13999      * @cfg {String} selectedClass The css class to add to selected nodes
14000      */
14001     selectedClass : "x-view-selected",
14002      /**
14003      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14004      */
14005     emptyText : "",
14006     
14007     /**
14008      * @cfg {String} text to display on mask (default Loading)
14009      */
14010     mask : false,
14011     /**
14012      * @cfg {Boolean} multiSelect Allow multiple selection
14013      */
14014     multiSelect : false,
14015     /**
14016      * @cfg {Boolean} singleSelect Allow single selection
14017      */
14018     singleSelect:  false,
14019     
14020     /**
14021      * @cfg {Boolean} toggleSelect - selecting 
14022      */
14023     toggleSelect : false,
14024     
14025     /**
14026      * @cfg {Boolean} tickable - selecting 
14027      */
14028     tickable : false,
14029     
14030     /**
14031      * Returns the element this view is bound to.
14032      * @return {Roo.Element}
14033      */
14034     getEl : function(){
14035         return this.wrapEl;
14036     },
14037     
14038     
14039
14040     /**
14041      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14042      */
14043     refresh : function(){
14044         //Roo.log('refresh');
14045         var t = this.tpl;
14046         
14047         // if we are using something like 'domtemplate', then
14048         // the what gets used is:
14049         // t.applySubtemplate(NAME, data, wrapping data..)
14050         // the outer template then get' applied with
14051         //     the store 'extra data'
14052         // and the body get's added to the
14053         //      roo-name="data" node?
14054         //      <span class='roo-tpl-{name}'></span> ?????
14055         
14056         
14057         
14058         this.clearSelections();
14059         this.el.update("");
14060         var html = [];
14061         var records = this.store.getRange();
14062         if(records.length < 1) {
14063             
14064             // is this valid??  = should it render a template??
14065             
14066             this.el.update(this.emptyText);
14067             return;
14068         }
14069         var el = this.el;
14070         if (this.dataName) {
14071             this.el.update(t.apply(this.store.meta)); //????
14072             el = this.el.child('.roo-tpl-' + this.dataName);
14073         }
14074         
14075         for(var i = 0, len = records.length; i < len; i++){
14076             var data = this.prepareData(records[i].data, i, records[i]);
14077             this.fireEvent("preparedata", this, data, i, records[i]);
14078             
14079             var d = Roo.apply({}, data);
14080             
14081             if(this.tickable){
14082                 Roo.apply(d, {'roo-id' : Roo.id()});
14083                 
14084                 var _this = this;
14085             
14086                 Roo.each(this.parent.item, function(item){
14087                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14088                         return;
14089                     }
14090                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14091                 });
14092             }
14093             
14094             html[html.length] = Roo.util.Format.trim(
14095                 this.dataName ?
14096                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14097                     t.apply(d)
14098             );
14099         }
14100         
14101         
14102         
14103         el.update(html.join(""));
14104         this.nodes = el.dom.childNodes;
14105         this.updateIndexes(0);
14106     },
14107     
14108
14109     /**
14110      * Function to override to reformat the data that is sent to
14111      * the template for each node.
14112      * DEPRICATED - use the preparedata event handler.
14113      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14114      * a JSON object for an UpdateManager bound view).
14115      */
14116     prepareData : function(data, index, record)
14117     {
14118         this.fireEvent("preparedata", this, data, index, record);
14119         return data;
14120     },
14121
14122     onUpdate : function(ds, record){
14123         // Roo.log('on update');   
14124         this.clearSelections();
14125         var index = this.store.indexOf(record);
14126         var n = this.nodes[index];
14127         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14128         n.parentNode.removeChild(n);
14129         this.updateIndexes(index, index);
14130     },
14131
14132     
14133     
14134 // --------- FIXME     
14135     onAdd : function(ds, records, index)
14136     {
14137         //Roo.log(['on Add', ds, records, index] );        
14138         this.clearSelections();
14139         if(this.nodes.length == 0){
14140             this.refresh();
14141             return;
14142         }
14143         var n = this.nodes[index];
14144         for(var i = 0, len = records.length; i < len; i++){
14145             var d = this.prepareData(records[i].data, i, records[i]);
14146             if(n){
14147                 this.tpl.insertBefore(n, d);
14148             }else{
14149                 
14150                 this.tpl.append(this.el, d);
14151             }
14152         }
14153         this.updateIndexes(index);
14154     },
14155
14156     onRemove : function(ds, record, index){
14157        // Roo.log('onRemove');
14158         this.clearSelections();
14159         var el = this.dataName  ?
14160             this.el.child('.roo-tpl-' + this.dataName) :
14161             this.el; 
14162         
14163         el.dom.removeChild(this.nodes[index]);
14164         this.updateIndexes(index);
14165     },
14166
14167     /**
14168      * Refresh an individual node.
14169      * @param {Number} index
14170      */
14171     refreshNode : function(index){
14172         this.onUpdate(this.store, this.store.getAt(index));
14173     },
14174
14175     updateIndexes : function(startIndex, endIndex){
14176         var ns = this.nodes;
14177         startIndex = startIndex || 0;
14178         endIndex = endIndex || ns.length - 1;
14179         for(var i = startIndex; i <= endIndex; i++){
14180             ns[i].nodeIndex = i;
14181         }
14182     },
14183
14184     /**
14185      * Changes the data store this view uses and refresh the view.
14186      * @param {Store} store
14187      */
14188     setStore : function(store, initial){
14189         if(!initial && this.store){
14190             this.store.un("datachanged", this.refresh);
14191             this.store.un("add", this.onAdd);
14192             this.store.un("remove", this.onRemove);
14193             this.store.un("update", this.onUpdate);
14194             this.store.un("clear", this.refresh);
14195             this.store.un("beforeload", this.onBeforeLoad);
14196             this.store.un("load", this.onLoad);
14197             this.store.un("loadexception", this.onLoad);
14198         }
14199         if(store){
14200           
14201             store.on("datachanged", this.refresh, this);
14202             store.on("add", this.onAdd, this);
14203             store.on("remove", this.onRemove, this);
14204             store.on("update", this.onUpdate, this);
14205             store.on("clear", this.refresh, this);
14206             store.on("beforeload", this.onBeforeLoad, this);
14207             store.on("load", this.onLoad, this);
14208             store.on("loadexception", this.onLoad, this);
14209         }
14210         
14211         if(store){
14212             this.refresh();
14213         }
14214     },
14215     /**
14216      * onbeforeLoad - masks the loading area.
14217      *
14218      */
14219     onBeforeLoad : function(store,opts)
14220     {
14221          //Roo.log('onBeforeLoad');   
14222         if (!opts.add) {
14223             this.el.update("");
14224         }
14225         this.el.mask(this.mask ? this.mask : "Loading" ); 
14226     },
14227     onLoad : function ()
14228     {
14229         this.el.unmask();
14230     },
14231     
14232
14233     /**
14234      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14235      * @param {HTMLElement} node
14236      * @return {HTMLElement} The template node
14237      */
14238     findItemFromChild : function(node){
14239         var el = this.dataName  ?
14240             this.el.child('.roo-tpl-' + this.dataName,true) :
14241             this.el.dom; 
14242         
14243         if(!node || node.parentNode == el){
14244                     return node;
14245             }
14246             var p = node.parentNode;
14247             while(p && p != el){
14248             if(p.parentNode == el){
14249                 return p;
14250             }
14251             p = p.parentNode;
14252         }
14253             return null;
14254     },
14255
14256     /** @ignore */
14257     onClick : function(e){
14258         var item = this.findItemFromChild(e.getTarget());
14259         if(item){
14260             var index = this.indexOf(item);
14261             if(this.onItemClick(item, index, e) !== false){
14262                 this.fireEvent("click", this, index, item, e);
14263             }
14264         }else{
14265             this.clearSelections();
14266         }
14267     },
14268
14269     /** @ignore */
14270     onContextMenu : function(e){
14271         var item = this.findItemFromChild(e.getTarget());
14272         if(item){
14273             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14274         }
14275     },
14276
14277     /** @ignore */
14278     onDblClick : function(e){
14279         var item = this.findItemFromChild(e.getTarget());
14280         if(item){
14281             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14282         }
14283     },
14284
14285     onItemClick : function(item, index, e)
14286     {
14287         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14288             return false;
14289         }
14290         if (this.toggleSelect) {
14291             var m = this.isSelected(item) ? 'unselect' : 'select';
14292             //Roo.log(m);
14293             var _t = this;
14294             _t[m](item, true, false);
14295             return true;
14296         }
14297         if(this.multiSelect || this.singleSelect){
14298             if(this.multiSelect && e.shiftKey && this.lastSelection){
14299                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14300             }else{
14301                 this.select(item, this.multiSelect && e.ctrlKey);
14302                 this.lastSelection = item;
14303             }
14304             
14305             if(!this.tickable){
14306                 e.preventDefault();
14307             }
14308             
14309         }
14310         return true;
14311     },
14312
14313     /**
14314      * Get the number of selected nodes.
14315      * @return {Number}
14316      */
14317     getSelectionCount : function(){
14318         return this.selections.length;
14319     },
14320
14321     /**
14322      * Get the currently selected nodes.
14323      * @return {Array} An array of HTMLElements
14324      */
14325     getSelectedNodes : function(){
14326         return this.selections;
14327     },
14328
14329     /**
14330      * Get the indexes of the selected nodes.
14331      * @return {Array}
14332      */
14333     getSelectedIndexes : function(){
14334         var indexes = [], s = this.selections;
14335         for(var i = 0, len = s.length; i < len; i++){
14336             indexes.push(s[i].nodeIndex);
14337         }
14338         return indexes;
14339     },
14340
14341     /**
14342      * Clear all selections
14343      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14344      */
14345     clearSelections : function(suppressEvent){
14346         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14347             this.cmp.elements = this.selections;
14348             this.cmp.removeClass(this.selectedClass);
14349             this.selections = [];
14350             if(!suppressEvent){
14351                 this.fireEvent("selectionchange", this, this.selections);
14352             }
14353         }
14354     },
14355
14356     /**
14357      * Returns true if the passed node is selected
14358      * @param {HTMLElement/Number} node The node or node index
14359      * @return {Boolean}
14360      */
14361     isSelected : function(node){
14362         var s = this.selections;
14363         if(s.length < 1){
14364             return false;
14365         }
14366         node = this.getNode(node);
14367         return s.indexOf(node) !== -1;
14368     },
14369
14370     /**
14371      * Selects nodes.
14372      * @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
14373      * @param {Boolean} keepExisting (optional) true to keep existing selections
14374      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14375      */
14376     select : function(nodeInfo, keepExisting, suppressEvent){
14377         if(nodeInfo instanceof Array){
14378             if(!keepExisting){
14379                 this.clearSelections(true);
14380             }
14381             for(var i = 0, len = nodeInfo.length; i < len; i++){
14382                 this.select(nodeInfo[i], true, true);
14383             }
14384             return;
14385         } 
14386         var node = this.getNode(nodeInfo);
14387         if(!node || this.isSelected(node)){
14388             return; // already selected.
14389         }
14390         if(!keepExisting){
14391             this.clearSelections(true);
14392         }
14393         
14394         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14395             Roo.fly(node).addClass(this.selectedClass);
14396             this.selections.push(node);
14397             if(!suppressEvent){
14398                 this.fireEvent("selectionchange", this, this.selections);
14399             }
14400         }
14401         
14402         
14403     },
14404       /**
14405      * Unselects nodes.
14406      * @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
14407      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14409      */
14410     unselect : function(nodeInfo, keepExisting, suppressEvent)
14411     {
14412         if(nodeInfo instanceof Array){
14413             Roo.each(this.selections, function(s) {
14414                 this.unselect(s, nodeInfo);
14415             }, this);
14416             return;
14417         }
14418         var node = this.getNode(nodeInfo);
14419         if(!node || !this.isSelected(node)){
14420             //Roo.log("not selected");
14421             return; // not selected.
14422         }
14423         // fireevent???
14424         var ns = [];
14425         Roo.each(this.selections, function(s) {
14426             if (s == node ) {
14427                 Roo.fly(node).removeClass(this.selectedClass);
14428
14429                 return;
14430             }
14431             ns.push(s);
14432         },this);
14433         
14434         this.selections= ns;
14435         this.fireEvent("selectionchange", this, this.selections);
14436     },
14437
14438     /**
14439      * Gets a template node.
14440      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14441      * @return {HTMLElement} The node or null if it wasn't found
14442      */
14443     getNode : function(nodeInfo){
14444         if(typeof nodeInfo == "string"){
14445             return document.getElementById(nodeInfo);
14446         }else if(typeof nodeInfo == "number"){
14447             return this.nodes[nodeInfo];
14448         }
14449         return nodeInfo;
14450     },
14451
14452     /**
14453      * Gets a range template nodes.
14454      * @param {Number} startIndex
14455      * @param {Number} endIndex
14456      * @return {Array} An array of nodes
14457      */
14458     getNodes : function(start, end){
14459         var ns = this.nodes;
14460         start = start || 0;
14461         end = typeof end == "undefined" ? ns.length - 1 : end;
14462         var nodes = [];
14463         if(start <= end){
14464             for(var i = start; i <= end; i++){
14465                 nodes.push(ns[i]);
14466             }
14467         } else{
14468             for(var i = start; i >= end; i--){
14469                 nodes.push(ns[i]);
14470             }
14471         }
14472         return nodes;
14473     },
14474
14475     /**
14476      * Finds the index of the passed node
14477      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14478      * @return {Number} The index of the node or -1
14479      */
14480     indexOf : function(node){
14481         node = this.getNode(node);
14482         if(typeof node.nodeIndex == "number"){
14483             return node.nodeIndex;
14484         }
14485         var ns = this.nodes;
14486         for(var i = 0, len = ns.length; i < len; i++){
14487             if(ns[i] == node){
14488                 return i;
14489             }
14490         }
14491         return -1;
14492     }
14493 });
14494 /*
14495  * - LGPL
14496  *
14497  * based on jquery fullcalendar
14498  * 
14499  */
14500
14501 Roo.bootstrap = Roo.bootstrap || {};
14502 /**
14503  * @class Roo.bootstrap.Calendar
14504  * @extends Roo.bootstrap.Component
14505  * Bootstrap Calendar class
14506  * @cfg {Boolean} loadMask (true|false) default false
14507  * @cfg {Object} header generate the user specific header of the calendar, default false
14508
14509  * @constructor
14510  * Create a new Container
14511  * @param {Object} config The config object
14512  */
14513
14514
14515
14516 Roo.bootstrap.Calendar = function(config){
14517     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14518      this.addEvents({
14519         /**
14520              * @event select
14521              * Fires when a date is selected
14522              * @param {DatePicker} this
14523              * @param {Date} date The selected date
14524              */
14525         'select': true,
14526         /**
14527              * @event monthchange
14528              * Fires when the displayed month changes 
14529              * @param {DatePicker} this
14530              * @param {Date} date The selected month
14531              */
14532         'monthchange': true,
14533         /**
14534              * @event evententer
14535              * Fires when mouse over an event
14536              * @param {Calendar} this
14537              * @param {event} Event
14538              */
14539         'evententer': true,
14540         /**
14541              * @event eventleave
14542              * Fires when the mouse leaves an
14543              * @param {Calendar} this
14544              * @param {event}
14545              */
14546         'eventleave': true,
14547         /**
14548              * @event eventclick
14549              * Fires when the mouse click an
14550              * @param {Calendar} this
14551              * @param {event}
14552              */
14553         'eventclick': true
14554         
14555     });
14556
14557 };
14558
14559 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14560     
14561      /**
14562      * @cfg {Number} startDay
14563      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14564      */
14565     startDay : 0,
14566     
14567     loadMask : false,
14568     
14569     header : false,
14570       
14571     getAutoCreate : function(){
14572         
14573         
14574         var fc_button = function(name, corner, style, content ) {
14575             return Roo.apply({},{
14576                 tag : 'span',
14577                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14578                          (corner.length ?
14579                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14580                             ''
14581                         ),
14582                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14583                 unselectable: 'on'
14584             });
14585         };
14586         
14587         var header = {};
14588         
14589         if(!this.header){
14590             header = {
14591                 tag : 'table',
14592                 cls : 'fc-header',
14593                 style : 'width:100%',
14594                 cn : [
14595                     {
14596                         tag: 'tr',
14597                         cn : [
14598                             {
14599                                 tag : 'td',
14600                                 cls : 'fc-header-left',
14601                                 cn : [
14602                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14603                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14604                                     { tag: 'span', cls: 'fc-header-space' },
14605                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14606
14607
14608                                 ]
14609                             },
14610
14611                             {
14612                                 tag : 'td',
14613                                 cls : 'fc-header-center',
14614                                 cn : [
14615                                     {
14616                                         tag: 'span',
14617                                         cls: 'fc-header-title',
14618                                         cn : {
14619                                             tag: 'H2',
14620                                             html : 'month / year'
14621                                         }
14622                                     }
14623
14624                                 ]
14625                             },
14626                             {
14627                                 tag : 'td',
14628                                 cls : 'fc-header-right',
14629                                 cn : [
14630                               /*      fc_button('month', 'left', '', 'month' ),
14631                                     fc_button('week', '', '', 'week' ),
14632                                     fc_button('day', 'right', '', 'day' )
14633                                 */    
14634
14635                                 ]
14636                             }
14637
14638                         ]
14639                     }
14640                 ]
14641             };
14642         }
14643         
14644         header = this.header;
14645         
14646        
14647         var cal_heads = function() {
14648             var ret = [];
14649             // fixme - handle this.
14650             
14651             for (var i =0; i < Date.dayNames.length; i++) {
14652                 var d = Date.dayNames[i];
14653                 ret.push({
14654                     tag: 'th',
14655                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14656                     html : d.substring(0,3)
14657                 });
14658                 
14659             }
14660             ret[0].cls += ' fc-first';
14661             ret[6].cls += ' fc-last';
14662             return ret;
14663         };
14664         var cal_cell = function(n) {
14665             return  {
14666                 tag: 'td',
14667                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14668                 cn : [
14669                     {
14670                         cn : [
14671                             {
14672                                 cls: 'fc-day-number',
14673                                 html: 'D'
14674                             },
14675                             {
14676                                 cls: 'fc-day-content',
14677                              
14678                                 cn : [
14679                                      {
14680                                         style: 'position: relative;' // height: 17px;
14681                                     }
14682                                 ]
14683                             }
14684                             
14685                             
14686                         ]
14687                     }
14688                 ]
14689                 
14690             }
14691         };
14692         var cal_rows = function() {
14693             
14694             var ret = [];
14695             for (var r = 0; r < 6; r++) {
14696                 var row= {
14697                     tag : 'tr',
14698                     cls : 'fc-week',
14699                     cn : []
14700                 };
14701                 
14702                 for (var i =0; i < Date.dayNames.length; i++) {
14703                     var d = Date.dayNames[i];
14704                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14705
14706                 }
14707                 row.cn[0].cls+=' fc-first';
14708                 row.cn[0].cn[0].style = 'min-height:90px';
14709                 row.cn[6].cls+=' fc-last';
14710                 ret.push(row);
14711                 
14712             }
14713             ret[0].cls += ' fc-first';
14714             ret[4].cls += ' fc-prev-last';
14715             ret[5].cls += ' fc-last';
14716             return ret;
14717             
14718         };
14719         
14720         var cal_table = {
14721             tag: 'table',
14722             cls: 'fc-border-separate',
14723             style : 'width:100%',
14724             cellspacing  : 0,
14725             cn : [
14726                 { 
14727                     tag: 'thead',
14728                     cn : [
14729                         { 
14730                             tag: 'tr',
14731                             cls : 'fc-first fc-last',
14732                             cn : cal_heads()
14733                         }
14734                     ]
14735                 },
14736                 { 
14737                     tag: 'tbody',
14738                     cn : cal_rows()
14739                 }
14740                   
14741             ]
14742         };
14743          
14744          var cfg = {
14745             cls : 'fc fc-ltr',
14746             cn : [
14747                 header,
14748                 {
14749                     cls : 'fc-content',
14750                     style : "position: relative;",
14751                     cn : [
14752                         {
14753                             cls : 'fc-view fc-view-month fc-grid',
14754                             style : 'position: relative',
14755                             unselectable : 'on',
14756                             cn : [
14757                                 {
14758                                     cls : 'fc-event-container',
14759                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14760                                 },
14761                                 cal_table
14762                             ]
14763                         }
14764                     ]
14765     
14766                 }
14767            ] 
14768             
14769         };
14770         
14771          
14772         
14773         return cfg;
14774     },
14775     
14776     
14777     initEvents : function()
14778     {
14779         if(!this.store){
14780             throw "can not find store for calendar";
14781         }
14782         
14783         var mark = {
14784             tag: "div",
14785             cls:"x-dlg-mask",
14786             style: "text-align:center",
14787             cn: [
14788                 {
14789                     tag: "div",
14790                     style: "background-color:white;width:50%;margin:250 auto",
14791                     cn: [
14792                         {
14793                             tag: "img",
14794                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14795                         },
14796                         {
14797                             tag: "span",
14798                             html: "Loading"
14799                         }
14800                         
14801                     ]
14802                 }
14803             ]
14804         };
14805         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14806         
14807         var size = this.el.select('.fc-content', true).first().getSize();
14808         this.maskEl.setSize(size.width, size.height);
14809         this.maskEl.enableDisplayMode("block");
14810         if(!this.loadMask){
14811             this.maskEl.hide();
14812         }
14813         
14814         this.store = Roo.factory(this.store, Roo.data);
14815         this.store.on('load', this.onLoad, this);
14816         this.store.on('beforeload', this.onBeforeLoad, this);
14817         
14818         this.resize();
14819         
14820         this.cells = this.el.select('.fc-day',true);
14821         //Roo.log(this.cells);
14822         this.textNodes = this.el.query('.fc-day-number');
14823         this.cells.addClassOnOver('fc-state-hover');
14824         
14825         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14826         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14827         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14828         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14829         
14830         this.on('monthchange', this.onMonthChange, this);
14831         
14832         this.update(new Date().clearTime());
14833     },
14834     
14835     resize : function() {
14836         var sz  = this.el.getSize();
14837         
14838         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14839         this.el.select('.fc-day-content div',true).setHeight(34);
14840     },
14841     
14842     
14843     // private
14844     showPrevMonth : function(e){
14845         this.update(this.activeDate.add("mo", -1));
14846     },
14847     showToday : function(e){
14848         this.update(new Date().clearTime());
14849     },
14850     // private
14851     showNextMonth : function(e){
14852         this.update(this.activeDate.add("mo", 1));
14853     },
14854
14855     // private
14856     showPrevYear : function(){
14857         this.update(this.activeDate.add("y", -1));
14858     },
14859
14860     // private
14861     showNextYear : function(){
14862         this.update(this.activeDate.add("y", 1));
14863     },
14864
14865     
14866    // private
14867     update : function(date)
14868     {
14869         var vd = this.activeDate;
14870         this.activeDate = date;
14871 //        if(vd && this.el){
14872 //            var t = date.getTime();
14873 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14874 //                Roo.log('using add remove');
14875 //                
14876 //                this.fireEvent('monthchange', this, date);
14877 //                
14878 //                this.cells.removeClass("fc-state-highlight");
14879 //                this.cells.each(function(c){
14880 //                   if(c.dateValue == t){
14881 //                       c.addClass("fc-state-highlight");
14882 //                       setTimeout(function(){
14883 //                            try{c.dom.firstChild.focus();}catch(e){}
14884 //                       }, 50);
14885 //                       return false;
14886 //                   }
14887 //                   return true;
14888 //                });
14889 //                return;
14890 //            }
14891 //        }
14892         
14893         var days = date.getDaysInMonth();
14894         
14895         var firstOfMonth = date.getFirstDateOfMonth();
14896         var startingPos = firstOfMonth.getDay()-this.startDay;
14897         
14898         if(startingPos < this.startDay){
14899             startingPos += 7;
14900         }
14901         
14902         var pm = date.add(Date.MONTH, -1);
14903         var prevStart = pm.getDaysInMonth()-startingPos;
14904 //        
14905         this.cells = this.el.select('.fc-day',true);
14906         this.textNodes = this.el.query('.fc-day-number');
14907         this.cells.addClassOnOver('fc-state-hover');
14908         
14909         var cells = this.cells.elements;
14910         var textEls = this.textNodes;
14911         
14912         Roo.each(cells, function(cell){
14913             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14914         });
14915         
14916         days += startingPos;
14917
14918         // convert everything to numbers so it's fast
14919         var day = 86400000;
14920         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14921         //Roo.log(d);
14922         //Roo.log(pm);
14923         //Roo.log(prevStart);
14924         
14925         var today = new Date().clearTime().getTime();
14926         var sel = date.clearTime().getTime();
14927         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14928         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14929         var ddMatch = this.disabledDatesRE;
14930         var ddText = this.disabledDatesText;
14931         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14932         var ddaysText = this.disabledDaysText;
14933         var format = this.format;
14934         
14935         var setCellClass = function(cal, cell){
14936             cell.row = 0;
14937             cell.events = [];
14938             cell.more = [];
14939             //Roo.log('set Cell Class');
14940             cell.title = "";
14941             var t = d.getTime();
14942             
14943             //Roo.log(d);
14944             
14945             cell.dateValue = t;
14946             if(t == today){
14947                 cell.className += " fc-today";
14948                 cell.className += " fc-state-highlight";
14949                 cell.title = cal.todayText;
14950             }
14951             if(t == sel){
14952                 // disable highlight in other month..
14953                 //cell.className += " fc-state-highlight";
14954                 
14955             }
14956             // disabling
14957             if(t < min) {
14958                 cell.className = " fc-state-disabled";
14959                 cell.title = cal.minText;
14960                 return;
14961             }
14962             if(t > max) {
14963                 cell.className = " fc-state-disabled";
14964                 cell.title = cal.maxText;
14965                 return;
14966             }
14967             if(ddays){
14968                 if(ddays.indexOf(d.getDay()) != -1){
14969                     cell.title = ddaysText;
14970                     cell.className = " fc-state-disabled";
14971                 }
14972             }
14973             if(ddMatch && format){
14974                 var fvalue = d.dateFormat(format);
14975                 if(ddMatch.test(fvalue)){
14976                     cell.title = ddText.replace("%0", fvalue);
14977                     cell.className = " fc-state-disabled";
14978                 }
14979             }
14980             
14981             if (!cell.initialClassName) {
14982                 cell.initialClassName = cell.dom.className;
14983             }
14984             
14985             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14986         };
14987
14988         var i = 0;
14989         
14990         for(; i < startingPos; i++) {
14991             textEls[i].innerHTML = (++prevStart);
14992             d.setDate(d.getDate()+1);
14993             
14994             cells[i].className = "fc-past fc-other-month";
14995             setCellClass(this, cells[i]);
14996         }
14997         
14998         var intDay = 0;
14999         
15000         for(; i < days; i++){
15001             intDay = i - startingPos + 1;
15002             textEls[i].innerHTML = (intDay);
15003             d.setDate(d.getDate()+1);
15004             
15005             cells[i].className = ''; // "x-date-active";
15006             setCellClass(this, cells[i]);
15007         }
15008         var extraDays = 0;
15009         
15010         for(; i < 42; i++) {
15011             textEls[i].innerHTML = (++extraDays);
15012             d.setDate(d.getDate()+1);
15013             
15014             cells[i].className = "fc-future fc-other-month";
15015             setCellClass(this, cells[i]);
15016         }
15017         
15018         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15019         
15020         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15021         
15022         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15023         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15024         
15025         if(totalRows != 6){
15026             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15027             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15028         }
15029         
15030         this.fireEvent('monthchange', this, date);
15031         
15032         
15033         /*
15034         if(!this.internalRender){
15035             var main = this.el.dom.firstChild;
15036             var w = main.offsetWidth;
15037             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15038             Roo.fly(main).setWidth(w);
15039             this.internalRender = true;
15040             // opera does not respect the auto grow header center column
15041             // then, after it gets a width opera refuses to recalculate
15042             // without a second pass
15043             if(Roo.isOpera && !this.secondPass){
15044                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15045                 this.secondPass = true;
15046                 this.update.defer(10, this, [date]);
15047             }
15048         }
15049         */
15050         
15051     },
15052     
15053     findCell : function(dt) {
15054         dt = dt.clearTime().getTime();
15055         var ret = false;
15056         this.cells.each(function(c){
15057             //Roo.log("check " +c.dateValue + '?=' + dt);
15058             if(c.dateValue == dt){
15059                 ret = c;
15060                 return false;
15061             }
15062             return true;
15063         });
15064         
15065         return ret;
15066     },
15067     
15068     findCells : function(ev) {
15069         var s = ev.start.clone().clearTime().getTime();
15070        // Roo.log(s);
15071         var e= ev.end.clone().clearTime().getTime();
15072        // Roo.log(e);
15073         var ret = [];
15074         this.cells.each(function(c){
15075              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15076             
15077             if(c.dateValue > e){
15078                 return ;
15079             }
15080             if(c.dateValue < s){
15081                 return ;
15082             }
15083             ret.push(c);
15084         });
15085         
15086         return ret;    
15087     },
15088     
15089 //    findBestRow: function(cells)
15090 //    {
15091 //        var ret = 0;
15092 //        
15093 //        for (var i =0 ; i < cells.length;i++) {
15094 //            ret  = Math.max(cells[i].rows || 0,ret);
15095 //        }
15096 //        return ret;
15097 //        
15098 //    },
15099     
15100     
15101     addItem : function(ev)
15102     {
15103         // look for vertical location slot in
15104         var cells = this.findCells(ev);
15105         
15106 //        ev.row = this.findBestRow(cells);
15107         
15108         // work out the location.
15109         
15110         var crow = false;
15111         var rows = [];
15112         for(var i =0; i < cells.length; i++) {
15113             
15114             cells[i].row = cells[0].row;
15115             
15116             if(i == 0){
15117                 cells[i].row = cells[i].row + 1;
15118             }
15119             
15120             if (!crow) {
15121                 crow = {
15122                     start : cells[i],
15123                     end :  cells[i]
15124                 };
15125                 continue;
15126             }
15127             if (crow.start.getY() == cells[i].getY()) {
15128                 // on same row.
15129                 crow.end = cells[i];
15130                 continue;
15131             }
15132             // different row.
15133             rows.push(crow);
15134             crow = {
15135                 start: cells[i],
15136                 end : cells[i]
15137             };
15138             
15139         }
15140         
15141         rows.push(crow);
15142         ev.els = [];
15143         ev.rows = rows;
15144         ev.cells = cells;
15145         
15146         cells[0].events.push(ev);
15147         
15148         this.calevents.push(ev);
15149     },
15150     
15151     clearEvents: function() {
15152         
15153         if(!this.calevents){
15154             return;
15155         }
15156         
15157         Roo.each(this.cells.elements, function(c){
15158             c.row = 0;
15159             c.events = [];
15160             c.more = [];
15161         });
15162         
15163         Roo.each(this.calevents, function(e) {
15164             Roo.each(e.els, function(el) {
15165                 el.un('mouseenter' ,this.onEventEnter, this);
15166                 el.un('mouseleave' ,this.onEventLeave, this);
15167                 el.remove();
15168             },this);
15169         },this);
15170         
15171         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15172             e.remove();
15173         });
15174         
15175     },
15176     
15177     renderEvents: function()
15178     {   
15179         var _this = this;
15180         
15181         this.cells.each(function(c) {
15182             
15183             if(c.row < 5){
15184                 return;
15185             }
15186             
15187             var ev = c.events;
15188             
15189             var r = 4;
15190             if(c.row != c.events.length){
15191                 r = 4 - (4 - (c.row - c.events.length));
15192             }
15193             
15194             c.events = ev.slice(0, r);
15195             c.more = ev.slice(r);
15196             
15197             if(c.more.length && c.more.length == 1){
15198                 c.events.push(c.more.pop());
15199             }
15200             
15201             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15202             
15203         });
15204             
15205         this.cells.each(function(c) {
15206             
15207             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15208             
15209             
15210             for (var e = 0; e < c.events.length; e++){
15211                 var ev = c.events[e];
15212                 var rows = ev.rows;
15213                 
15214                 for(var i = 0; i < rows.length; i++) {
15215                 
15216                     // how many rows should it span..
15217
15218                     var  cfg = {
15219                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15220                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15221
15222                         unselectable : "on",
15223                         cn : [
15224                             {
15225                                 cls: 'fc-event-inner',
15226                                 cn : [
15227     //                                {
15228     //                                  tag:'span',
15229     //                                  cls: 'fc-event-time',
15230     //                                  html : cells.length > 1 ? '' : ev.time
15231     //                                },
15232                                     {
15233                                       tag:'span',
15234                                       cls: 'fc-event-title',
15235                                       html : String.format('{0}', ev.title)
15236                                     }
15237
15238
15239                                 ]
15240                             },
15241                             {
15242                                 cls: 'ui-resizable-handle ui-resizable-e',
15243                                 html : '&nbsp;&nbsp;&nbsp'
15244                             }
15245
15246                         ]
15247                     };
15248
15249                     if (i == 0) {
15250                         cfg.cls += ' fc-event-start';
15251                     }
15252                     if ((i+1) == rows.length) {
15253                         cfg.cls += ' fc-event-end';
15254                     }
15255
15256                     var ctr = _this.el.select('.fc-event-container',true).first();
15257                     var cg = ctr.createChild(cfg);
15258
15259                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15260                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15261
15262                     var r = (c.more.length) ? 1 : 0;
15263                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15264                     cg.setWidth(ebox.right - sbox.x -2);
15265
15266                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15267                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15268                     cg.on('click', _this.onEventClick, _this, ev);
15269
15270                     ev.els.push(cg);
15271                     
15272                 }
15273                 
15274             }
15275             
15276             
15277             if(c.more.length){
15278                 var  cfg = {
15279                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15280                     style : 'position: absolute',
15281                     unselectable : "on",
15282                     cn : [
15283                         {
15284                             cls: 'fc-event-inner',
15285                             cn : [
15286                                 {
15287                                   tag:'span',
15288                                   cls: 'fc-event-title',
15289                                   html : 'More'
15290                                 }
15291
15292
15293                             ]
15294                         },
15295                         {
15296                             cls: 'ui-resizable-handle ui-resizable-e',
15297                             html : '&nbsp;&nbsp;&nbsp'
15298                         }
15299
15300                     ]
15301                 };
15302
15303                 var ctr = _this.el.select('.fc-event-container',true).first();
15304                 var cg = ctr.createChild(cfg);
15305
15306                 var sbox = c.select('.fc-day-content',true).first().getBox();
15307                 var ebox = c.select('.fc-day-content',true).first().getBox();
15308                 //Roo.log(cg);
15309                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15310                 cg.setWidth(ebox.right - sbox.x -2);
15311
15312                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15313                 
15314             }
15315             
15316         });
15317         
15318         
15319         
15320     },
15321     
15322     onEventEnter: function (e, el,event,d) {
15323         this.fireEvent('evententer', this, el, event);
15324     },
15325     
15326     onEventLeave: function (e, el,event,d) {
15327         this.fireEvent('eventleave', this, el, event);
15328     },
15329     
15330     onEventClick: function (e, el,event,d) {
15331         this.fireEvent('eventclick', this, el, event);
15332     },
15333     
15334     onMonthChange: function () {
15335         this.store.load();
15336     },
15337     
15338     onMoreEventClick: function(e, el, more)
15339     {
15340         var _this = this;
15341         
15342         this.calpopover.placement = 'right';
15343         this.calpopover.setTitle('More');
15344         
15345         this.calpopover.setContent('');
15346         
15347         var ctr = this.calpopover.el.select('.popover-content', true).first();
15348         
15349         Roo.each(more, function(m){
15350             var cfg = {
15351                 cls : 'fc-event-hori fc-event-draggable',
15352                 html : m.title
15353             };
15354             var cg = ctr.createChild(cfg);
15355             
15356             cg.on('click', _this.onEventClick, _this, m);
15357         });
15358         
15359         this.calpopover.show(el);
15360         
15361         
15362     },
15363     
15364     onLoad: function () 
15365     {   
15366         this.calevents = [];
15367         var cal = this;
15368         
15369         if(this.store.getCount() > 0){
15370             this.store.data.each(function(d){
15371                cal.addItem({
15372                     id : d.data.id,
15373                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15374                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15375                     time : d.data.start_time,
15376                     title : d.data.title,
15377                     description : d.data.description,
15378                     venue : d.data.venue
15379                 });
15380             });
15381         }
15382         
15383         this.renderEvents();
15384         
15385         if(this.calevents.length && this.loadMask){
15386             this.maskEl.hide();
15387         }
15388     },
15389     
15390     onBeforeLoad: function()
15391     {
15392         this.clearEvents();
15393         if(this.loadMask){
15394             this.maskEl.show();
15395         }
15396     }
15397 });
15398
15399  
15400  /*
15401  * - LGPL
15402  *
15403  * element
15404  * 
15405  */
15406
15407 /**
15408  * @class Roo.bootstrap.Popover
15409  * @extends Roo.bootstrap.Component
15410  * Bootstrap Popover class
15411  * @cfg {String} html contents of the popover   (or false to use children..)
15412  * @cfg {String} title of popover (or false to hide)
15413  * @cfg {String} placement how it is placed
15414  * @cfg {String} trigger click || hover (or false to trigger manually)
15415  * @cfg {String} over what (parent or false to trigger manually.)
15416  * @cfg {Number} delay - delay before showing
15417  
15418  * @constructor
15419  * Create a new Popover
15420  * @param {Object} config The config object
15421  */
15422
15423 Roo.bootstrap.Popover = function(config){
15424     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15425 };
15426
15427 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15428     
15429     title: 'Fill in a title',
15430     html: false,
15431     
15432     placement : 'right',
15433     trigger : 'hover', // hover
15434     
15435     delay : 0,
15436     
15437     over: 'parent',
15438     
15439     can_build_overlaid : false,
15440     
15441     getChildContainer : function()
15442     {
15443         return this.el.select('.popover-content',true).first();
15444     },
15445     
15446     getAutoCreate : function(){
15447          Roo.log('make popover?');
15448         var cfg = {
15449            cls : 'popover roo-dynamic',
15450            style: 'display:block',
15451            cn : [
15452                 {
15453                     cls : 'arrow'
15454                 },
15455                 {
15456                     cls : 'popover-inner',
15457                     cn : [
15458                         {
15459                             tag: 'h3',
15460                             cls: 'popover-title',
15461                             html : this.title
15462                         },
15463                         {
15464                             cls : 'popover-content',
15465                             html : this.html
15466                         }
15467                     ]
15468                     
15469                 }
15470            ]
15471         };
15472         
15473         return cfg;
15474     },
15475     setTitle: function(str)
15476     {
15477         this.title = str;
15478         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15479     },
15480     setContent: function(str)
15481     {
15482         this.html = str;
15483         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15484     },
15485     // as it get's added to the bottom of the page.
15486     onRender : function(ct, position)
15487     {
15488         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15489         if(!this.el){
15490             var cfg = Roo.apply({},  this.getAutoCreate());
15491             cfg.id = Roo.id();
15492             
15493             if (this.cls) {
15494                 cfg.cls += ' ' + this.cls;
15495             }
15496             if (this.style) {
15497                 cfg.style = this.style;
15498             }
15499             Roo.log("adding to ")
15500             this.el = Roo.get(document.body).createChild(cfg, position);
15501             Roo.log(this.el);
15502         }
15503         this.initEvents();
15504     },
15505     
15506     initEvents : function()
15507     {
15508         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15509         this.el.enableDisplayMode('block');
15510         this.el.hide();
15511         if (this.over === false) {
15512             return; 
15513         }
15514         if (this.triggers === false) {
15515             return;
15516         }
15517         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15518         var triggers = this.trigger ? this.trigger.split(' ') : [];
15519         Roo.each(triggers, function(trigger) {
15520         
15521             if (trigger == 'click') {
15522                 on_el.on('click', this.toggle, this);
15523             } else if (trigger != 'manual') {
15524                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15525                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15526       
15527                 on_el.on(eventIn  ,this.enter, this);
15528                 on_el.on(eventOut, this.leave, this);
15529             }
15530         }, this);
15531         
15532     },
15533     
15534     
15535     // private
15536     timeout : null,
15537     hoverState : null,
15538     
15539     toggle : function () {
15540         this.hoverState == 'in' ? this.leave() : this.enter();
15541     },
15542     
15543     enter : function () {
15544        
15545     
15546         clearTimeout(this.timeout);
15547     
15548         this.hoverState = 'in';
15549     
15550         if (!this.delay || !this.delay.show) {
15551             this.show();
15552             return;
15553         }
15554         var _t = this;
15555         this.timeout = setTimeout(function () {
15556             if (_t.hoverState == 'in') {
15557                 _t.show();
15558             }
15559         }, this.delay.show)
15560     },
15561     leave : function() {
15562         clearTimeout(this.timeout);
15563     
15564         this.hoverState = 'out';
15565     
15566         if (!this.delay || !this.delay.hide) {
15567             this.hide();
15568             return;
15569         }
15570         var _t = this;
15571         this.timeout = setTimeout(function () {
15572             if (_t.hoverState == 'out') {
15573                 _t.hide();
15574             }
15575         }, this.delay.hide)
15576     },
15577     
15578     show : function (on_el)
15579     {
15580         if (!on_el) {
15581             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15582         }
15583         // set content.
15584         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15585         if (this.html !== false) {
15586             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15587         }
15588         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15589         if (!this.title.length) {
15590             this.el.select('.popover-title',true).hide();
15591         }
15592         
15593         var placement = typeof this.placement == 'function' ?
15594             this.placement.call(this, this.el, on_el) :
15595             this.placement;
15596             
15597         var autoToken = /\s?auto?\s?/i;
15598         var autoPlace = autoToken.test(placement);
15599         if (autoPlace) {
15600             placement = placement.replace(autoToken, '') || 'top';
15601         }
15602         
15603         //this.el.detach()
15604         //this.el.setXY([0,0]);
15605         this.el.show();
15606         this.el.dom.style.display='block';
15607         this.el.addClass(placement);
15608         
15609         //this.el.appendTo(on_el);
15610         
15611         var p = this.getPosition();
15612         var box = this.el.getBox();
15613         
15614         if (autoPlace) {
15615             // fixme..
15616         }
15617         var align = Roo.bootstrap.Popover.alignment[placement];
15618         this.el.alignTo(on_el, align[0],align[1]);
15619         //var arrow = this.el.select('.arrow',true).first();
15620         //arrow.set(align[2], 
15621         
15622         this.el.addClass('in');
15623         
15624         
15625         if (this.el.hasClass('fade')) {
15626             // fade it?
15627         }
15628         
15629     },
15630     hide : function()
15631     {
15632         this.el.setXY([0,0]);
15633         this.el.removeClass('in');
15634         this.el.hide();
15635         this.hoverState = null;
15636         
15637     }
15638     
15639 });
15640
15641 Roo.bootstrap.Popover.alignment = {
15642     'left' : ['r-l', [-10,0], 'right'],
15643     'right' : ['l-r', [10,0], 'left'],
15644     'bottom' : ['t-b', [0,10], 'top'],
15645     'top' : [ 'b-t', [0,-10], 'bottom']
15646 };
15647
15648  /*
15649  * - LGPL
15650  *
15651  * Progress
15652  * 
15653  */
15654
15655 /**
15656  * @class Roo.bootstrap.Progress
15657  * @extends Roo.bootstrap.Component
15658  * Bootstrap Progress class
15659  * @cfg {Boolean} striped striped of the progress bar
15660  * @cfg {Boolean} active animated of the progress bar
15661  * 
15662  * 
15663  * @constructor
15664  * Create a new Progress
15665  * @param {Object} config The config object
15666  */
15667
15668 Roo.bootstrap.Progress = function(config){
15669     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15670 };
15671
15672 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15673     
15674     striped : false,
15675     active: false,
15676     
15677     getAutoCreate : function(){
15678         var cfg = {
15679             tag: 'div',
15680             cls: 'progress'
15681         };
15682         
15683         
15684         if(this.striped){
15685             cfg.cls += ' progress-striped';
15686         }
15687       
15688         if(this.active){
15689             cfg.cls += ' active';
15690         }
15691         
15692         
15693         return cfg;
15694     }
15695    
15696 });
15697
15698  
15699
15700  /*
15701  * - LGPL
15702  *
15703  * ProgressBar
15704  * 
15705  */
15706
15707 /**
15708  * @class Roo.bootstrap.ProgressBar
15709  * @extends Roo.bootstrap.Component
15710  * Bootstrap ProgressBar class
15711  * @cfg {Number} aria_valuenow aria-value now
15712  * @cfg {Number} aria_valuemin aria-value min
15713  * @cfg {Number} aria_valuemax aria-value max
15714  * @cfg {String} label label for the progress bar
15715  * @cfg {String} panel (success | info | warning | danger )
15716  * @cfg {String} role role of the progress bar
15717  * @cfg {String} sr_only text
15718  * 
15719  * 
15720  * @constructor
15721  * Create a new ProgressBar
15722  * @param {Object} config The config object
15723  */
15724
15725 Roo.bootstrap.ProgressBar = function(config){
15726     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15727 };
15728
15729 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15730     
15731     aria_valuenow : 0,
15732     aria_valuemin : 0,
15733     aria_valuemax : 100,
15734     label : false,
15735     panel : false,
15736     role : false,
15737     sr_only: false,
15738     
15739     getAutoCreate : function()
15740     {
15741         
15742         var cfg = {
15743             tag: 'div',
15744             cls: 'progress-bar',
15745             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15746         };
15747         
15748         if(this.sr_only){
15749             cfg.cn = {
15750                 tag: 'span',
15751                 cls: 'sr-only',
15752                 html: this.sr_only
15753             }
15754         }
15755         
15756         if(this.role){
15757             cfg.role = this.role;
15758         }
15759         
15760         if(this.aria_valuenow){
15761             cfg['aria-valuenow'] = this.aria_valuenow;
15762         }
15763         
15764         if(this.aria_valuemin){
15765             cfg['aria-valuemin'] = this.aria_valuemin;
15766         }
15767         
15768         if(this.aria_valuemax){
15769             cfg['aria-valuemax'] = this.aria_valuemax;
15770         }
15771         
15772         if(this.label && !this.sr_only){
15773             cfg.html = this.label;
15774         }
15775         
15776         if(this.panel){
15777             cfg.cls += ' progress-bar-' + this.panel;
15778         }
15779         
15780         return cfg;
15781     },
15782     
15783     update : function(aria_valuenow)
15784     {
15785         this.aria_valuenow = aria_valuenow;
15786         
15787         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15788     }
15789    
15790 });
15791
15792  
15793
15794  /*
15795  * - LGPL
15796  *
15797  * column
15798  * 
15799  */
15800
15801 /**
15802  * @class Roo.bootstrap.TabGroup
15803  * @extends Roo.bootstrap.Column
15804  * Bootstrap Column class
15805  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15806  * @cfg {Boolean} carousel true to make the group behave like a carousel
15807  * @cfg {Number} bullets show the panel pointer.. default 0
15808  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15809  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15810  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15811  * 
15812  * @constructor
15813  * Create a new TabGroup
15814  * @param {Object} config The config object
15815  */
15816
15817 Roo.bootstrap.TabGroup = function(config){
15818     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15819     if (!this.navId) {
15820         this.navId = Roo.id();
15821     }
15822     this.tabs = [];
15823     Roo.bootstrap.TabGroup.register(this);
15824     
15825 };
15826
15827 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15828     
15829     carousel : false,
15830     transition : false,
15831     bullets : 0,
15832     timer : 0,
15833     autoslide : false,
15834     slideFn : false,
15835     slideOnTouch : false,
15836     
15837     getAutoCreate : function()
15838     {
15839         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15840         
15841         cfg.cls += ' tab-content';
15842         
15843         Roo.log('get auto create...............');
15844         
15845         if (this.carousel) {
15846             cfg.cls += ' carousel slide';
15847             
15848             cfg.cn = [{
15849                cls : 'carousel-inner'
15850             }];
15851         
15852             if(this.bullets > 0 && !Roo.isTouch){
15853                 
15854                 var bullets = {
15855                     cls : 'carousel-bullets',
15856                     cn : []
15857                 };
15858                 
15859                 if(this.bullets_cls){
15860                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15861                 }
15862                 
15863                 for (var i = 0; i < this.bullets; i++){
15864                     bullets.cn.push({
15865                         cls : 'bullet bullet-' + i
15866                     });
15867                 }
15868                 
15869                 bullets.cn.push({
15870                     cls : 'clear'
15871                 });
15872                 
15873                 cfg.cn[0].cn = bullets;
15874             }
15875         }
15876         
15877         return cfg;
15878     },
15879     
15880     initEvents:  function()
15881     {
15882         Roo.log('-------- init events on tab group ---------');
15883         
15884         if(this.bullets > 0 && !Roo.isTouch){
15885             this.initBullet();
15886         }
15887         
15888         Roo.log(this);
15889         
15890         if(Roo.isTouch && this.slideOnTouch){
15891             this.el.on("touchstart", this.onTouchStart, this);
15892         }
15893         
15894         if(this.autoslide){
15895             var _this = this;
15896             
15897             this.slideFn = window.setInterval(function() {
15898                 _this.showPanelNext();
15899             }, this.timer);
15900         }
15901         
15902     },
15903     
15904     onTouchStart : function(e, el, o)
15905     {
15906         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15907             return;
15908         }
15909         
15910         this.showPanelNext();
15911     },
15912     
15913     getChildContainer : function()
15914     {
15915         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15916     },
15917     
15918     /**
15919     * register a Navigation item
15920     * @param {Roo.bootstrap.NavItem} the navitem to add
15921     */
15922     register : function(item)
15923     {
15924         this.tabs.push( item);
15925         item.navId = this.navId; // not really needed..
15926     
15927     },
15928     
15929     getActivePanel : function()
15930     {
15931         var r = false;
15932         Roo.each(this.tabs, function(t) {
15933             if (t.active) {
15934                 r = t;
15935                 return false;
15936             }
15937             return null;
15938         });
15939         return r;
15940         
15941     },
15942     getPanelByName : function(n)
15943     {
15944         var r = false;
15945         Roo.each(this.tabs, function(t) {
15946             if (t.tabId == n) {
15947                 r = t;
15948                 return false;
15949             }
15950             return null;
15951         });
15952         return r;
15953     },
15954     indexOfPanel : function(p)
15955     {
15956         var r = false;
15957         Roo.each(this.tabs, function(t,i) {
15958             if (t.tabId == p.tabId) {
15959                 r = i;
15960                 return false;
15961             }
15962             return null;
15963         });
15964         return r;
15965     },
15966     /**
15967      * show a specific panel
15968      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15969      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15970      */
15971     showPanel : function (pan)
15972     {
15973         if(this.transition){
15974             Roo.log("waiting for the transitionend");
15975             return;
15976         }
15977         
15978         if (typeof(pan) == 'number') {
15979             pan = this.tabs[pan];
15980         }
15981         if (typeof(pan) == 'string') {
15982             pan = this.getPanelByName(pan);
15983         }
15984         if (pan.tabId == this.getActivePanel().tabId) {
15985             return true;
15986         }
15987         var cur = this.getActivePanel();
15988         
15989         if (false === cur.fireEvent('beforedeactivate')) {
15990             return false;
15991         }
15992         
15993         if(this.bullets > 0 && !Roo.isTouch){
15994             this.setActiveBullet(this.indexOfPanel(pan));
15995         }
15996         
15997         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15998             
15999             this.transition = true;
16000             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16001             var lr = dir == 'next' ? 'left' : 'right';
16002             pan.el.addClass(dir); // or prev
16003             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16004             cur.el.addClass(lr); // or right
16005             pan.el.addClass(lr);
16006             
16007             var _this = this;
16008             cur.el.on('transitionend', function() {
16009                 Roo.log("trans end?");
16010                 
16011                 pan.el.removeClass([lr,dir]);
16012                 pan.setActive(true);
16013                 
16014                 cur.el.removeClass([lr]);
16015                 cur.setActive(false);
16016                 
16017                 _this.transition = false;
16018                 
16019             }, this, { single:  true } );
16020             
16021             return true;
16022         }
16023         
16024         cur.setActive(false);
16025         pan.setActive(true);
16026         
16027         return true;
16028         
16029     },
16030     showPanelNext : function()
16031     {
16032         var i = this.indexOfPanel(this.getActivePanel());
16033         
16034         if (i >= this.tabs.length - 1 && !this.autoslide) {
16035             return;
16036         }
16037         
16038         if (i >= this.tabs.length - 1 && this.autoslide) {
16039             i = -1;
16040         }
16041         
16042         this.showPanel(this.tabs[i+1]);
16043     },
16044     
16045     showPanelPrev : function()
16046     {
16047         var i = this.indexOfPanel(this.getActivePanel());
16048         
16049         if (i  < 1 && !this.autoslide) {
16050             return;
16051         }
16052         
16053         if (i < 1 && this.autoslide) {
16054             i = this.tabs.length;
16055         }
16056         
16057         this.showPanel(this.tabs[i-1]);
16058     },
16059     
16060     initBullet : function()
16061     {
16062         if(Roo.isTouch){
16063             return;
16064         }
16065         
16066         var _this = this;
16067         
16068         for (var i = 0; i < this.bullets; i++){
16069             var bullet = this.el.select('.bullet-' + i, true).first();
16070
16071             if(!bullet){
16072                 continue;
16073             }
16074
16075             bullet.on('click', (function(e, el, o, ii, t){
16076
16077                 e.preventDefault();
16078
16079                 _this.showPanel(ii);
16080
16081                 if(_this.autoslide && _this.slideFn){
16082                     clearInterval(_this.slideFn);
16083                     _this.slideFn = window.setInterval(function() {
16084                         _this.showPanelNext();
16085                     }, _this.timer);
16086                 }
16087
16088             }).createDelegate(this, [i, bullet], true));
16089         }
16090     },
16091     
16092     setActiveBullet : function(i)
16093     {
16094         if(Roo.isTouch){
16095             return;
16096         }
16097         
16098         Roo.each(this.el.select('.bullet', true).elements, function(el){
16099             el.removeClass('selected');
16100         });
16101
16102         var bullet = this.el.select('.bullet-' + i, true).first();
16103         
16104         if(!bullet){
16105             return;
16106         }
16107         
16108         bullet.addClass('selected');
16109     }
16110     
16111     
16112   
16113 });
16114
16115  
16116
16117  
16118  
16119 Roo.apply(Roo.bootstrap.TabGroup, {
16120     
16121     groups: {},
16122      /**
16123     * register a Navigation Group
16124     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16125     */
16126     register : function(navgrp)
16127     {
16128         this.groups[navgrp.navId] = navgrp;
16129         
16130     },
16131     /**
16132     * fetch a Navigation Group based on the navigation ID
16133     * if one does not exist , it will get created.
16134     * @param {string} the navgroup to add
16135     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16136     */
16137     get: function(navId) {
16138         if (typeof(this.groups[navId]) == 'undefined') {
16139             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16140         }
16141         return this.groups[navId] ;
16142     }
16143     
16144     
16145     
16146 });
16147
16148  /*
16149  * - LGPL
16150  *
16151  * TabPanel
16152  * 
16153  */
16154
16155 /**
16156  * @class Roo.bootstrap.TabPanel
16157  * @extends Roo.bootstrap.Component
16158  * Bootstrap TabPanel class
16159  * @cfg {Boolean} active panel active
16160  * @cfg {String} html panel content
16161  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16162  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16163  * 
16164  * 
16165  * @constructor
16166  * Create a new TabPanel
16167  * @param {Object} config The config object
16168  */
16169
16170 Roo.bootstrap.TabPanel = function(config){
16171     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16172     this.addEvents({
16173         /**
16174              * @event changed
16175              * Fires when the active status changes
16176              * @param {Roo.bootstrap.TabPanel} this
16177              * @param {Boolean} state the new state
16178             
16179          */
16180         'changed': true,
16181         /**
16182              * @event beforedeactivate
16183              * Fires before a tab is de-activated - can be used to do validation on a form.
16184              * @param {Roo.bootstrap.TabPanel} this
16185              * @return {Boolean} false if there is an error
16186             
16187          */
16188         'beforedeactivate': true
16189      });
16190     
16191     this.tabId = this.tabId || Roo.id();
16192   
16193 };
16194
16195 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16196     
16197     active: false,
16198     html: false,
16199     tabId: false,
16200     navId : false,
16201     
16202     getAutoCreate : function(){
16203         var cfg = {
16204             tag: 'div',
16205             // item is needed for carousel - not sure if it has any effect otherwise
16206             cls: 'tab-pane item',
16207             html: this.html || ''
16208         };
16209         
16210         if(this.active){
16211             cfg.cls += ' active';
16212         }
16213         
16214         if(this.tabId){
16215             cfg.tabId = this.tabId;
16216         }
16217         
16218         
16219         return cfg;
16220     },
16221     
16222     initEvents:  function()
16223     {
16224         Roo.log('-------- init events on tab panel ---------');
16225         
16226         var p = this.parent();
16227         this.navId = this.navId || p.navId;
16228         
16229         if (typeof(this.navId) != 'undefined') {
16230             // not really needed.. but just in case.. parent should be a NavGroup.
16231             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16232             Roo.log(['register', tg, this]);
16233             tg.register(this);
16234             
16235             var i = tg.tabs.length - 1;
16236             
16237             if(this.active && tg.bullets > 0 && i < tg.bullets){
16238                 tg.setActiveBullet(i);
16239             }
16240         }
16241         
16242     },
16243     
16244     
16245     onRender : function(ct, position)
16246     {
16247        // Roo.log("Call onRender: " + this.xtype);
16248         
16249         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16250         
16251         
16252         
16253         
16254         
16255     },
16256     
16257     setActive: function(state)
16258     {
16259         Roo.log("panel - set active " + this.tabId + "=" + state);
16260         
16261         this.active = state;
16262         if (!state) {
16263             this.el.removeClass('active');
16264             
16265         } else  if (!this.el.hasClass('active')) {
16266             this.el.addClass('active');
16267         }
16268         
16269         this.fireEvent('changed', this, state);
16270     }
16271     
16272     
16273 });
16274  
16275
16276  
16277
16278  /*
16279  * - LGPL
16280  *
16281  * DateField
16282  * 
16283  */
16284
16285 /**
16286  * @class Roo.bootstrap.DateField
16287  * @extends Roo.bootstrap.Input
16288  * Bootstrap DateField class
16289  * @cfg {Number} weekStart default 0
16290  * @cfg {String} viewMode default empty, (months|years)
16291  * @cfg {String} minViewMode default empty, (months|years)
16292  * @cfg {Number} startDate default -Infinity
16293  * @cfg {Number} endDate default Infinity
16294  * @cfg {Boolean} todayHighlight default false
16295  * @cfg {Boolean} todayBtn default false
16296  * @cfg {Boolean} calendarWeeks default false
16297  * @cfg {Object} daysOfWeekDisabled default empty
16298  * @cfg {Boolean} singleMode default false (true | false)
16299  * 
16300  * @cfg {Boolean} keyboardNavigation default true
16301  * @cfg {String} language default en
16302  * 
16303  * @constructor
16304  * Create a new DateField
16305  * @param {Object} config The config object
16306  */
16307
16308 Roo.bootstrap.DateField = function(config){
16309     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16310      this.addEvents({
16311             /**
16312              * @event show
16313              * Fires when this field show.
16314              * @param {Roo.bootstrap.DateField} this
16315              * @param {Mixed} date The date value
16316              */
16317             show : true,
16318             /**
16319              * @event show
16320              * Fires when this field hide.
16321              * @param {Roo.bootstrap.DateField} this
16322              * @param {Mixed} date The date value
16323              */
16324             hide : true,
16325             /**
16326              * @event select
16327              * Fires when select a date.
16328              * @param {Roo.bootstrap.DateField} this
16329              * @param {Mixed} date The date value
16330              */
16331             select : true,
16332             /**
16333              * @event beforeselect
16334              * Fires when before select a date.
16335              * @param {Roo.bootstrap.DateField} this
16336              * @param {Mixed} date The date value
16337              */
16338             beforeselect : true
16339         });
16340 };
16341
16342 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16343     
16344     /**
16345      * @cfg {String} format
16346      * The default date format string which can be overriden for localization support.  The format must be
16347      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16348      */
16349     format : "m/d/y",
16350     /**
16351      * @cfg {String} altFormats
16352      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16353      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16354      */
16355     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16356     
16357     weekStart : 0,
16358     
16359     viewMode : '',
16360     
16361     minViewMode : '',
16362     
16363     todayHighlight : false,
16364     
16365     todayBtn: false,
16366     
16367     language: 'en',
16368     
16369     keyboardNavigation: true,
16370     
16371     calendarWeeks: false,
16372     
16373     startDate: -Infinity,
16374     
16375     endDate: Infinity,
16376     
16377     daysOfWeekDisabled: [],
16378     
16379     _events: [],
16380     
16381     singleMode : false,
16382     
16383     UTCDate: function()
16384     {
16385         return new Date(Date.UTC.apply(Date, arguments));
16386     },
16387     
16388     UTCToday: function()
16389     {
16390         var today = new Date();
16391         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16392     },
16393     
16394     getDate: function() {
16395             var d = this.getUTCDate();
16396             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16397     },
16398     
16399     getUTCDate: function() {
16400             return this.date;
16401     },
16402     
16403     setDate: function(d) {
16404             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16405     },
16406     
16407     setUTCDate: function(d) {
16408             this.date = d;
16409             this.setValue(this.formatDate(this.date));
16410     },
16411         
16412     onRender: function(ct, position)
16413     {
16414         
16415         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16416         
16417         this.language = this.language || 'en';
16418         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16419         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16420         
16421         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16422         this.format = this.format || 'm/d/y';
16423         this.isInline = false;
16424         this.isInput = true;
16425         this.component = this.el.select('.add-on', true).first() || false;
16426         this.component = (this.component && this.component.length === 0) ? false : this.component;
16427         this.hasInput = this.component && this.inputEL().length;
16428         
16429         if (typeof(this.minViewMode === 'string')) {
16430             switch (this.minViewMode) {
16431                 case 'months':
16432                     this.minViewMode = 1;
16433                     break;
16434                 case 'years':
16435                     this.minViewMode = 2;
16436                     break;
16437                 default:
16438                     this.minViewMode = 0;
16439                     break;
16440             }
16441         }
16442         
16443         if (typeof(this.viewMode === 'string')) {
16444             switch (this.viewMode) {
16445                 case 'months':
16446                     this.viewMode = 1;
16447                     break;
16448                 case 'years':
16449                     this.viewMode = 2;
16450                     break;
16451                 default:
16452                     this.viewMode = 0;
16453                     break;
16454             }
16455         }
16456                 
16457         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16458         
16459 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16460         
16461         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16462         
16463         this.picker().on('mousedown', this.onMousedown, this);
16464         this.picker().on('click', this.onClick, this);
16465         
16466         this.picker().addClass('datepicker-dropdown');
16467         
16468         this.startViewMode = this.viewMode;
16469         
16470         if(this.singleMode){
16471             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16472                 v.setVisibilityMode(Roo.Element.DISPLAY)
16473                 v.hide();
16474             });
16475             
16476             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16477                 v.setStyle('width', '189px');
16478             });
16479         }
16480         
16481         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16482             if(!this.calendarWeeks){
16483                 v.remove();
16484                 return;
16485             }
16486             
16487             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16488             v.attr('colspan', function(i, val){
16489                 return parseInt(val) + 1;
16490             });
16491         })
16492                         
16493         
16494         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16495         
16496         this.setStartDate(this.startDate);
16497         this.setEndDate(this.endDate);
16498         
16499         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16500         
16501         this.fillDow();
16502         this.fillMonths();
16503         this.update();
16504         this.showMode();
16505         
16506         if(this.isInline) {
16507             this.show();
16508         }
16509     },
16510     
16511     picker : function()
16512     {
16513         return this.pickerEl;
16514 //        return this.el.select('.datepicker', true).first();
16515     },
16516     
16517     fillDow: function()
16518     {
16519         var dowCnt = this.weekStart;
16520         
16521         var dow = {
16522             tag: 'tr',
16523             cn: [
16524                 
16525             ]
16526         };
16527         
16528         if(this.calendarWeeks){
16529             dow.cn.push({
16530                 tag: 'th',
16531                 cls: 'cw',
16532                 html: '&nbsp;'
16533             })
16534         }
16535         
16536         while (dowCnt < this.weekStart + 7) {
16537             dow.cn.push({
16538                 tag: 'th',
16539                 cls: 'dow',
16540                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16541             });
16542         }
16543         
16544         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16545     },
16546     
16547     fillMonths: function()
16548     {    
16549         var i = 0;
16550         var months = this.picker().select('>.datepicker-months td', true).first();
16551         
16552         months.dom.innerHTML = '';
16553         
16554         while (i < 12) {
16555             var month = {
16556                 tag: 'span',
16557                 cls: 'month',
16558                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16559             };
16560             
16561             months.createChild(month);
16562         }
16563         
16564     },
16565     
16566     update: function()
16567     {
16568         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;
16569         
16570         if (this.date < this.startDate) {
16571             this.viewDate = new Date(this.startDate);
16572         } else if (this.date > this.endDate) {
16573             this.viewDate = new Date(this.endDate);
16574         } else {
16575             this.viewDate = new Date(this.date);
16576         }
16577         
16578         this.fill();
16579     },
16580     
16581     fill: function() 
16582     {
16583         var d = new Date(this.viewDate),
16584                 year = d.getUTCFullYear(),
16585                 month = d.getUTCMonth(),
16586                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16587                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16588                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16589                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16590                 currentDate = this.date && this.date.valueOf(),
16591                 today = this.UTCToday();
16592         
16593         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16594         
16595 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16596         
16597 //        this.picker.select('>tfoot th.today').
16598 //                                              .text(dates[this.language].today)
16599 //                                              .toggle(this.todayBtn !== false);
16600     
16601         this.updateNavArrows();
16602         this.fillMonths();
16603                                                 
16604         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16605         
16606         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16607          
16608         prevMonth.setUTCDate(day);
16609         
16610         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16611         
16612         var nextMonth = new Date(prevMonth);
16613         
16614         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16615         
16616         nextMonth = nextMonth.valueOf();
16617         
16618         var fillMonths = false;
16619         
16620         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16621         
16622         while(prevMonth.valueOf() < nextMonth) {
16623             var clsName = '';
16624             
16625             if (prevMonth.getUTCDay() === this.weekStart) {
16626                 if(fillMonths){
16627                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16628                 }
16629                     
16630                 fillMonths = {
16631                     tag: 'tr',
16632                     cn: []
16633                 };
16634                 
16635                 if(this.calendarWeeks){
16636                     // ISO 8601: First week contains first thursday.
16637                     // ISO also states week starts on Monday, but we can be more abstract here.
16638                     var
16639                     // Start of current week: based on weekstart/current date
16640                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16641                     // Thursday of this week
16642                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16643                     // First Thursday of year, year from thursday
16644                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16645                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16646                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16647                     
16648                     fillMonths.cn.push({
16649                         tag: 'td',
16650                         cls: 'cw',
16651                         html: calWeek
16652                     });
16653                 }
16654             }
16655             
16656             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16657                 clsName += ' old';
16658             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16659                 clsName += ' new';
16660             }
16661             if (this.todayHighlight &&
16662                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16663                 prevMonth.getUTCMonth() == today.getMonth() &&
16664                 prevMonth.getUTCDate() == today.getDate()) {
16665                 clsName += ' today';
16666             }
16667             
16668             if (currentDate && prevMonth.valueOf() === currentDate) {
16669                 clsName += ' active';
16670             }
16671             
16672             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16673                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16674                     clsName += ' disabled';
16675             }
16676             
16677             fillMonths.cn.push({
16678                 tag: 'td',
16679                 cls: 'day ' + clsName,
16680                 html: prevMonth.getDate()
16681             })
16682             
16683             prevMonth.setDate(prevMonth.getDate()+1);
16684         }
16685           
16686         var currentYear = this.date && this.date.getUTCFullYear();
16687         var currentMonth = this.date && this.date.getUTCMonth();
16688         
16689         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16690         
16691         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16692             v.removeClass('active');
16693             
16694             if(currentYear === year && k === currentMonth){
16695                 v.addClass('active');
16696             }
16697             
16698             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16699                 v.addClass('disabled');
16700             }
16701             
16702         });
16703         
16704         
16705         year = parseInt(year/10, 10) * 10;
16706         
16707         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16708         
16709         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16710         
16711         year -= 1;
16712         for (var i = -1; i < 11; i++) {
16713             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16714                 tag: 'span',
16715                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16716                 html: year
16717             })
16718             
16719             year += 1;
16720         }
16721     },
16722     
16723     showMode: function(dir) 
16724     {
16725         if (dir) {
16726             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16727         }
16728         
16729         Roo.each(this.picker().select('>div',true).elements, function(v){
16730             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16731             v.hide();
16732         });
16733         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16734     },
16735     
16736     place: function()
16737     {
16738         if(this.isInline) return;
16739         
16740         this.picker().removeClass(['bottom', 'top']);
16741         
16742         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16743             /*
16744              * place to the top of element!
16745              *
16746              */
16747             
16748             this.picker().addClass('top');
16749             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16750             
16751             return;
16752         }
16753         
16754         this.picker().addClass('bottom');
16755         
16756         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16757     },
16758     
16759     parseDate : function(value)
16760     {
16761         if(!value || value instanceof Date){
16762             return value;
16763         }
16764         var v = Date.parseDate(value, this.format);
16765         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16766             v = Date.parseDate(value, 'Y-m-d');
16767         }
16768         if(!v && this.altFormats){
16769             if(!this.altFormatsArray){
16770                 this.altFormatsArray = this.altFormats.split("|");
16771             }
16772             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16773                 v = Date.parseDate(value, this.altFormatsArray[i]);
16774             }
16775         }
16776         return v;
16777     },
16778     
16779     formatDate : function(date, fmt)
16780     {   
16781         return (!date || !(date instanceof Date)) ?
16782         date : date.dateFormat(fmt || this.format);
16783     },
16784     
16785     onFocus : function()
16786     {
16787         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16788         this.show();
16789     },
16790     
16791     onBlur : function()
16792     {
16793         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16794         
16795         var d = this.inputEl().getValue();
16796         
16797         this.setValue(d);
16798                 
16799         this.hide();
16800     },
16801     
16802     show : function()
16803     {
16804         this.picker().show();
16805         this.update();
16806         this.place();
16807         
16808         this.fireEvent('show', this, this.date);
16809     },
16810     
16811     hide : function()
16812     {
16813         if(this.isInline) return;
16814         this.picker().hide();
16815         this.viewMode = this.startViewMode;
16816         this.showMode();
16817         
16818         this.fireEvent('hide', this, this.date);
16819         
16820     },
16821     
16822     onMousedown: function(e)
16823     {
16824         e.stopPropagation();
16825         e.preventDefault();
16826     },
16827     
16828     keyup: function(e)
16829     {
16830         Roo.bootstrap.DateField.superclass.keyup.call(this);
16831         this.update();
16832     },
16833
16834     setValue: function(v)
16835     {
16836         if(this.fireEvent('beforeselect', this, v) !== false){
16837             var d = new Date(this.parseDate(v) ).clearTime();
16838         
16839             if(isNaN(d.getTime())){
16840                 this.date = this.viewDate = '';
16841                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16842                 return;
16843             }
16844
16845             v = this.formatDate(d);
16846
16847             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16848
16849             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16850
16851             this.update();
16852
16853             this.fireEvent('select', this, this.date);
16854         }
16855     },
16856     
16857     getValue: function()
16858     {
16859         return this.formatDate(this.date);
16860     },
16861     
16862     fireKey: function(e)
16863     {
16864         if (!this.picker().isVisible()){
16865             if (e.keyCode == 27) // allow escape to hide and re-show picker
16866                 this.show();
16867             return;
16868         }
16869         
16870         var dateChanged = false,
16871         dir, day, month,
16872         newDate, newViewDate;
16873         
16874         switch(e.keyCode){
16875             case 27: // escape
16876                 this.hide();
16877                 e.preventDefault();
16878                 break;
16879             case 37: // left
16880             case 39: // right
16881                 if (!this.keyboardNavigation) break;
16882                 dir = e.keyCode == 37 ? -1 : 1;
16883                 
16884                 if (e.ctrlKey){
16885                     newDate = this.moveYear(this.date, dir);
16886                     newViewDate = this.moveYear(this.viewDate, dir);
16887                 } else if (e.shiftKey){
16888                     newDate = this.moveMonth(this.date, dir);
16889                     newViewDate = this.moveMonth(this.viewDate, dir);
16890                 } else {
16891                     newDate = new Date(this.date);
16892                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16893                     newViewDate = new Date(this.viewDate);
16894                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16895                 }
16896                 if (this.dateWithinRange(newDate)){
16897                     this.date = newDate;
16898                     this.viewDate = newViewDate;
16899                     this.setValue(this.formatDate(this.date));
16900 //                    this.update();
16901                     e.preventDefault();
16902                     dateChanged = true;
16903                 }
16904                 break;
16905             case 38: // up
16906             case 40: // down
16907                 if (!this.keyboardNavigation) break;
16908                 dir = e.keyCode == 38 ? -1 : 1;
16909                 if (e.ctrlKey){
16910                     newDate = this.moveYear(this.date, dir);
16911                     newViewDate = this.moveYear(this.viewDate, dir);
16912                 } else if (e.shiftKey){
16913                     newDate = this.moveMonth(this.date, dir);
16914                     newViewDate = this.moveMonth(this.viewDate, dir);
16915                 } else {
16916                     newDate = new Date(this.date);
16917                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16918                     newViewDate = new Date(this.viewDate);
16919                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16920                 }
16921                 if (this.dateWithinRange(newDate)){
16922                     this.date = newDate;
16923                     this.viewDate = newViewDate;
16924                     this.setValue(this.formatDate(this.date));
16925 //                    this.update();
16926                     e.preventDefault();
16927                     dateChanged = true;
16928                 }
16929                 break;
16930             case 13: // enter
16931                 this.setValue(this.formatDate(this.date));
16932                 this.hide();
16933                 e.preventDefault();
16934                 break;
16935             case 9: // tab
16936                 this.setValue(this.formatDate(this.date));
16937                 this.hide();
16938                 break;
16939             case 16: // shift
16940             case 17: // ctrl
16941             case 18: // alt
16942                 break;
16943             default :
16944                 this.hide();
16945                 
16946         }
16947     },
16948     
16949     
16950     onClick: function(e) 
16951     {
16952         e.stopPropagation();
16953         e.preventDefault();
16954         
16955         var target = e.getTarget();
16956         
16957         if(target.nodeName.toLowerCase() === 'i'){
16958             target = Roo.get(target).dom.parentNode;
16959         }
16960         
16961         var nodeName = target.nodeName;
16962         var className = target.className;
16963         var html = target.innerHTML;
16964         //Roo.log(nodeName);
16965         
16966         switch(nodeName.toLowerCase()) {
16967             case 'th':
16968                 switch(className) {
16969                     case 'switch':
16970                         this.showMode(1);
16971                         break;
16972                     case 'prev':
16973                     case 'next':
16974                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16975                         switch(this.viewMode){
16976                                 case 0:
16977                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16978                                         break;
16979                                 case 1:
16980                                 case 2:
16981                                         this.viewDate = this.moveYear(this.viewDate, dir);
16982                                         break;
16983                         }
16984                         this.fill();
16985                         break;
16986                     case 'today':
16987                         var date = new Date();
16988                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16989 //                        this.fill()
16990                         this.setValue(this.formatDate(this.date));
16991                         
16992                         this.hide();
16993                         break;
16994                 }
16995                 break;
16996             case 'span':
16997                 if (className.indexOf('disabled') < 0) {
16998                     this.viewDate.setUTCDate(1);
16999                     if (className.indexOf('month') > -1) {
17000                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17001                     } else {
17002                         var year = parseInt(html, 10) || 0;
17003                         this.viewDate.setUTCFullYear(year);
17004                         
17005                     }
17006                     
17007                     if(this.singleMode){
17008                         this.setValue(this.formatDate(this.viewDate));
17009                         this.hide();
17010                         return;
17011                     }
17012                     
17013                     this.showMode(-1);
17014                     this.fill();
17015                 }
17016                 break;
17017                 
17018             case 'td':
17019                 //Roo.log(className);
17020                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17021                     var day = parseInt(html, 10) || 1;
17022                     var year = this.viewDate.getUTCFullYear(),
17023                         month = this.viewDate.getUTCMonth();
17024
17025                     if (className.indexOf('old') > -1) {
17026                         if(month === 0 ){
17027                             month = 11;
17028                             year -= 1;
17029                         }else{
17030                             month -= 1;
17031                         }
17032                     } else if (className.indexOf('new') > -1) {
17033                         if (month == 11) {
17034                             month = 0;
17035                             year += 1;
17036                         } else {
17037                             month += 1;
17038                         }
17039                     }
17040                     //Roo.log([year,month,day]);
17041                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17042                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17043 //                    this.fill();
17044                     //Roo.log(this.formatDate(this.date));
17045                     this.setValue(this.formatDate(this.date));
17046                     this.hide();
17047                 }
17048                 break;
17049         }
17050     },
17051     
17052     setStartDate: function(startDate)
17053     {
17054         this.startDate = startDate || -Infinity;
17055         if (this.startDate !== -Infinity) {
17056             this.startDate = this.parseDate(this.startDate);
17057         }
17058         this.update();
17059         this.updateNavArrows();
17060     },
17061
17062     setEndDate: function(endDate)
17063     {
17064         this.endDate = endDate || Infinity;
17065         if (this.endDate !== Infinity) {
17066             this.endDate = this.parseDate(this.endDate);
17067         }
17068         this.update();
17069         this.updateNavArrows();
17070     },
17071     
17072     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17073     {
17074         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17075         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17076             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17077         }
17078         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17079             return parseInt(d, 10);
17080         });
17081         this.update();
17082         this.updateNavArrows();
17083     },
17084     
17085     updateNavArrows: function() 
17086     {
17087         if(this.singleMode){
17088             return;
17089         }
17090         
17091         var d = new Date(this.viewDate),
17092         year = d.getUTCFullYear(),
17093         month = d.getUTCMonth();
17094         
17095         Roo.each(this.picker().select('.prev', true).elements, function(v){
17096             v.show();
17097             switch (this.viewMode) {
17098                 case 0:
17099
17100                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17101                         v.hide();
17102                     }
17103                     break;
17104                 case 1:
17105                 case 2:
17106                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17107                         v.hide();
17108                     }
17109                     break;
17110             }
17111         });
17112         
17113         Roo.each(this.picker().select('.next', true).elements, function(v){
17114             v.show();
17115             switch (this.viewMode) {
17116                 case 0:
17117
17118                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17119                         v.hide();
17120                     }
17121                     break;
17122                 case 1:
17123                 case 2:
17124                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17125                         v.hide();
17126                     }
17127                     break;
17128             }
17129         })
17130     },
17131     
17132     moveMonth: function(date, dir)
17133     {
17134         if (!dir) return date;
17135         var new_date = new Date(date.valueOf()),
17136         day = new_date.getUTCDate(),
17137         month = new_date.getUTCMonth(),
17138         mag = Math.abs(dir),
17139         new_month, test;
17140         dir = dir > 0 ? 1 : -1;
17141         if (mag == 1){
17142             test = dir == -1
17143             // If going back one month, make sure month is not current month
17144             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17145             ? function(){
17146                 return new_date.getUTCMonth() == month;
17147             }
17148             // If going forward one month, make sure month is as expected
17149             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17150             : function(){
17151                 return new_date.getUTCMonth() != new_month;
17152             };
17153             new_month = month + dir;
17154             new_date.setUTCMonth(new_month);
17155             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17156             if (new_month < 0 || new_month > 11)
17157                 new_month = (new_month + 12) % 12;
17158         } else {
17159             // For magnitudes >1, move one month at a time...
17160             for (var i=0; i<mag; i++)
17161                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17162                 new_date = this.moveMonth(new_date, dir);
17163             // ...then reset the day, keeping it in the new month
17164             new_month = new_date.getUTCMonth();
17165             new_date.setUTCDate(day);
17166             test = function(){
17167                 return new_month != new_date.getUTCMonth();
17168             };
17169         }
17170         // Common date-resetting loop -- if date is beyond end of month, make it
17171         // end of month
17172         while (test()){
17173             new_date.setUTCDate(--day);
17174             new_date.setUTCMonth(new_month);
17175         }
17176         return new_date;
17177     },
17178
17179     moveYear: function(date, dir)
17180     {
17181         return this.moveMonth(date, dir*12);
17182     },
17183
17184     dateWithinRange: function(date)
17185     {
17186         return date >= this.startDate && date <= this.endDate;
17187     },
17188
17189     
17190     remove: function() 
17191     {
17192         this.picker().remove();
17193     }
17194    
17195 });
17196
17197 Roo.apply(Roo.bootstrap.DateField,  {
17198     
17199     head : {
17200         tag: 'thead',
17201         cn: [
17202         {
17203             tag: 'tr',
17204             cn: [
17205             {
17206                 tag: 'th',
17207                 cls: 'prev',
17208                 html: '<i class="fa fa-arrow-left"/>'
17209             },
17210             {
17211                 tag: 'th',
17212                 cls: 'switch',
17213                 colspan: '5'
17214             },
17215             {
17216                 tag: 'th',
17217                 cls: 'next',
17218                 html: '<i class="fa fa-arrow-right"/>'
17219             }
17220
17221             ]
17222         }
17223         ]
17224     },
17225     
17226     content : {
17227         tag: 'tbody',
17228         cn: [
17229         {
17230             tag: 'tr',
17231             cn: [
17232             {
17233                 tag: 'td',
17234                 colspan: '7'
17235             }
17236             ]
17237         }
17238         ]
17239     },
17240     
17241     footer : {
17242         tag: 'tfoot',
17243         cn: [
17244         {
17245             tag: 'tr',
17246             cn: [
17247             {
17248                 tag: 'th',
17249                 colspan: '7',
17250                 cls: 'today'
17251             }
17252                     
17253             ]
17254         }
17255         ]
17256     },
17257     
17258     dates:{
17259         en: {
17260             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17261             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17262             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17263             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17264             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17265             today: "Today"
17266         }
17267     },
17268     
17269     modes: [
17270     {
17271         clsName: 'days',
17272         navFnc: 'Month',
17273         navStep: 1
17274     },
17275     {
17276         clsName: 'months',
17277         navFnc: 'FullYear',
17278         navStep: 1
17279     },
17280     {
17281         clsName: 'years',
17282         navFnc: 'FullYear',
17283         navStep: 10
17284     }]
17285 });
17286
17287 Roo.apply(Roo.bootstrap.DateField,  {
17288   
17289     template : {
17290         tag: 'div',
17291         cls: 'datepicker dropdown-menu roo-dynamic',
17292         cn: [
17293         {
17294             tag: 'div',
17295             cls: 'datepicker-days',
17296             cn: [
17297             {
17298                 tag: 'table',
17299                 cls: 'table-condensed',
17300                 cn:[
17301                 Roo.bootstrap.DateField.head,
17302                 {
17303                     tag: 'tbody'
17304                 },
17305                 Roo.bootstrap.DateField.footer
17306                 ]
17307             }
17308             ]
17309         },
17310         {
17311             tag: 'div',
17312             cls: 'datepicker-months',
17313             cn: [
17314             {
17315                 tag: 'table',
17316                 cls: 'table-condensed',
17317                 cn:[
17318                 Roo.bootstrap.DateField.head,
17319                 Roo.bootstrap.DateField.content,
17320                 Roo.bootstrap.DateField.footer
17321                 ]
17322             }
17323             ]
17324         },
17325         {
17326             tag: 'div',
17327             cls: 'datepicker-years',
17328             cn: [
17329             {
17330                 tag: 'table',
17331                 cls: 'table-condensed',
17332                 cn:[
17333                 Roo.bootstrap.DateField.head,
17334                 Roo.bootstrap.DateField.content,
17335                 Roo.bootstrap.DateField.footer
17336                 ]
17337             }
17338             ]
17339         }
17340         ]
17341     }
17342 });
17343
17344  
17345
17346  /*
17347  * - LGPL
17348  *
17349  * TimeField
17350  * 
17351  */
17352
17353 /**
17354  * @class Roo.bootstrap.TimeField
17355  * @extends Roo.bootstrap.Input
17356  * Bootstrap DateField class
17357  * 
17358  * 
17359  * @constructor
17360  * Create a new TimeField
17361  * @param {Object} config The config object
17362  */
17363
17364 Roo.bootstrap.TimeField = function(config){
17365     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17366     this.addEvents({
17367             /**
17368              * @event show
17369              * Fires when this field show.
17370              * @param {Roo.bootstrap.DateField} thisthis
17371              * @param {Mixed} date The date value
17372              */
17373             show : true,
17374             /**
17375              * @event show
17376              * Fires when this field hide.
17377              * @param {Roo.bootstrap.DateField} this
17378              * @param {Mixed} date The date value
17379              */
17380             hide : true,
17381             /**
17382              * @event select
17383              * Fires when select a date.
17384              * @param {Roo.bootstrap.DateField} this
17385              * @param {Mixed} date The date value
17386              */
17387             select : true
17388         });
17389 };
17390
17391 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17392     
17393     /**
17394      * @cfg {String} format
17395      * The default time format string which can be overriden for localization support.  The format must be
17396      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17397      */
17398     format : "H:i",
17399        
17400     onRender: function(ct, position)
17401     {
17402         
17403         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17404                 
17405         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17406         
17407         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17408         
17409         this.pop = this.picker().select('>.datepicker-time',true).first();
17410         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17411         
17412         this.picker().on('mousedown', this.onMousedown, this);
17413         this.picker().on('click', this.onClick, this);
17414         
17415         this.picker().addClass('datepicker-dropdown');
17416     
17417         this.fillTime();
17418         this.update();
17419             
17420         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17421         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17422         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17423         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17424         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17425         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17426
17427     },
17428     
17429     fireKey: function(e){
17430         if (!this.picker().isVisible()){
17431             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17432                 this.show();
17433             }
17434             return;
17435         }
17436
17437         e.preventDefault();
17438         
17439         switch(e.keyCode){
17440             case 27: // escape
17441                 this.hide();
17442                 break;
17443             case 37: // left
17444             case 39: // right
17445                 this.onTogglePeriod();
17446                 break;
17447             case 38: // up
17448                 this.onIncrementMinutes();
17449                 break;
17450             case 40: // down
17451                 this.onDecrementMinutes();
17452                 break;
17453             case 13: // enter
17454             case 9: // tab
17455                 this.setTime();
17456                 break;
17457         }
17458     },
17459     
17460     onClick: function(e) {
17461         e.stopPropagation();
17462         e.preventDefault();
17463     },
17464     
17465     picker : function()
17466     {
17467         return this.el.select('.datepicker', true).first();
17468     },
17469     
17470     fillTime: function()
17471     {    
17472         var time = this.pop.select('tbody', true).first();
17473         
17474         time.dom.innerHTML = '';
17475         
17476         time.createChild({
17477             tag: 'tr',
17478             cn: [
17479                 {
17480                     tag: 'td',
17481                     cn: [
17482                         {
17483                             tag: 'a',
17484                             href: '#',
17485                             cls: 'btn',
17486                             cn: [
17487                                 {
17488                                     tag: 'span',
17489                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17490                                 }
17491                             ]
17492                         } 
17493                     ]
17494                 },
17495                 {
17496                     tag: 'td',
17497                     cls: 'separator'
17498                 },
17499                 {
17500                     tag: 'td',
17501                     cn: [
17502                         {
17503                             tag: 'a',
17504                             href: '#',
17505                             cls: 'btn',
17506                             cn: [
17507                                 {
17508                                     tag: 'span',
17509                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17510                                 }
17511                             ]
17512                         }
17513                     ]
17514                 },
17515                 {
17516                     tag: 'td',
17517                     cls: 'separator'
17518                 }
17519             ]
17520         });
17521         
17522         time.createChild({
17523             tag: 'tr',
17524             cn: [
17525                 {
17526                     tag: 'td',
17527                     cn: [
17528                         {
17529                             tag: 'span',
17530                             cls: 'timepicker-hour',
17531                             html: '00'
17532                         }  
17533                     ]
17534                 },
17535                 {
17536                     tag: 'td',
17537                     cls: 'separator',
17538                     html: ':'
17539                 },
17540                 {
17541                     tag: 'td',
17542                     cn: [
17543                         {
17544                             tag: 'span',
17545                             cls: 'timepicker-minute',
17546                             html: '00'
17547                         }  
17548                     ]
17549                 },
17550                 {
17551                     tag: 'td',
17552                     cls: 'separator'
17553                 },
17554                 {
17555                     tag: 'td',
17556                     cn: [
17557                         {
17558                             tag: 'button',
17559                             type: 'button',
17560                             cls: 'btn btn-primary period',
17561                             html: 'AM'
17562                             
17563                         }
17564                     ]
17565                 }
17566             ]
17567         });
17568         
17569         time.createChild({
17570             tag: 'tr',
17571             cn: [
17572                 {
17573                     tag: 'td',
17574                     cn: [
17575                         {
17576                             tag: 'a',
17577                             href: '#',
17578                             cls: 'btn',
17579                             cn: [
17580                                 {
17581                                     tag: 'span',
17582                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17583                                 }
17584                             ]
17585                         }
17586                     ]
17587                 },
17588                 {
17589                     tag: 'td',
17590                     cls: 'separator'
17591                 },
17592                 {
17593                     tag: 'td',
17594                     cn: [
17595                         {
17596                             tag: 'a',
17597                             href: '#',
17598                             cls: 'btn',
17599                             cn: [
17600                                 {
17601                                     tag: 'span',
17602                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17603                                 }
17604                             ]
17605                         }
17606                     ]
17607                 },
17608                 {
17609                     tag: 'td',
17610                     cls: 'separator'
17611                 }
17612             ]
17613         });
17614         
17615     },
17616     
17617     update: function()
17618     {
17619         
17620         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17621         
17622         this.fill();
17623     },
17624     
17625     fill: function() 
17626     {
17627         var hours = this.time.getHours();
17628         var minutes = this.time.getMinutes();
17629         var period = 'AM';
17630         
17631         if(hours > 11){
17632             period = 'PM';
17633         }
17634         
17635         if(hours == 0){
17636             hours = 12;
17637         }
17638         
17639         
17640         if(hours > 12){
17641             hours = hours - 12;
17642         }
17643         
17644         if(hours < 10){
17645             hours = '0' + hours;
17646         }
17647         
17648         if(minutes < 10){
17649             minutes = '0' + minutes;
17650         }
17651         
17652         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17653         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17654         this.pop.select('button', true).first().dom.innerHTML = period;
17655         
17656     },
17657     
17658     place: function()
17659     {   
17660         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17661         
17662         var cls = ['bottom'];
17663         
17664         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17665             cls.pop();
17666             cls.push('top');
17667         }
17668         
17669         cls.push('right');
17670         
17671         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17672             cls.pop();
17673             cls.push('left');
17674         }
17675         
17676         this.picker().addClass(cls.join('-'));
17677         
17678         var _this = this;
17679         
17680         Roo.each(cls, function(c){
17681             if(c == 'bottom'){
17682                 _this.picker().setTop(_this.inputEl().getHeight());
17683                 return;
17684             }
17685             if(c == 'top'){
17686                 _this.picker().setTop(0 - _this.picker().getHeight());
17687                 return;
17688             }
17689             
17690             if(c == 'left'){
17691                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17692                 return;
17693             }
17694             if(c == 'right'){
17695                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17696                 return;
17697             }
17698         });
17699         
17700     },
17701   
17702     onFocus : function()
17703     {
17704         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17705         this.show();
17706     },
17707     
17708     onBlur : function()
17709     {
17710         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17711         this.hide();
17712     },
17713     
17714     show : function()
17715     {
17716         this.picker().show();
17717         this.pop.show();
17718         this.update();
17719         this.place();
17720         
17721         this.fireEvent('show', this, this.date);
17722     },
17723     
17724     hide : function()
17725     {
17726         this.picker().hide();
17727         this.pop.hide();
17728         
17729         this.fireEvent('hide', this, this.date);
17730     },
17731     
17732     setTime : function()
17733     {
17734         this.hide();
17735         this.setValue(this.time.format(this.format));
17736         
17737         this.fireEvent('select', this, this.date);
17738         
17739         
17740     },
17741     
17742     onMousedown: function(e){
17743         e.stopPropagation();
17744         e.preventDefault();
17745     },
17746     
17747     onIncrementHours: function()
17748     {
17749         Roo.log('onIncrementHours');
17750         this.time = this.time.add(Date.HOUR, 1);
17751         this.update();
17752         
17753     },
17754     
17755     onDecrementHours: function()
17756     {
17757         Roo.log('onDecrementHours');
17758         this.time = this.time.add(Date.HOUR, -1);
17759         this.update();
17760     },
17761     
17762     onIncrementMinutes: function()
17763     {
17764         Roo.log('onIncrementMinutes');
17765         this.time = this.time.add(Date.MINUTE, 1);
17766         this.update();
17767     },
17768     
17769     onDecrementMinutes: function()
17770     {
17771         Roo.log('onDecrementMinutes');
17772         this.time = this.time.add(Date.MINUTE, -1);
17773         this.update();
17774     },
17775     
17776     onTogglePeriod: function()
17777     {
17778         Roo.log('onTogglePeriod');
17779         this.time = this.time.add(Date.HOUR, 12);
17780         this.update();
17781     }
17782     
17783    
17784 });
17785
17786 Roo.apply(Roo.bootstrap.TimeField,  {
17787     
17788     content : {
17789         tag: 'tbody',
17790         cn: [
17791             {
17792                 tag: 'tr',
17793                 cn: [
17794                 {
17795                     tag: 'td',
17796                     colspan: '7'
17797                 }
17798                 ]
17799             }
17800         ]
17801     },
17802     
17803     footer : {
17804         tag: 'tfoot',
17805         cn: [
17806             {
17807                 tag: 'tr',
17808                 cn: [
17809                 {
17810                     tag: 'th',
17811                     colspan: '7',
17812                     cls: '',
17813                     cn: [
17814                         {
17815                             tag: 'button',
17816                             cls: 'btn btn-info ok',
17817                             html: 'OK'
17818                         }
17819                     ]
17820                 }
17821
17822                 ]
17823             }
17824         ]
17825     }
17826 });
17827
17828 Roo.apply(Roo.bootstrap.TimeField,  {
17829   
17830     template : {
17831         tag: 'div',
17832         cls: 'datepicker dropdown-menu',
17833         cn: [
17834             {
17835                 tag: 'div',
17836                 cls: 'datepicker-time',
17837                 cn: [
17838                 {
17839                     tag: 'table',
17840                     cls: 'table-condensed',
17841                     cn:[
17842                     Roo.bootstrap.TimeField.content,
17843                     Roo.bootstrap.TimeField.footer
17844                     ]
17845                 }
17846                 ]
17847             }
17848         ]
17849     }
17850 });
17851
17852  
17853
17854  /*
17855  * - LGPL
17856  *
17857  * MonthField
17858  * 
17859  */
17860
17861 /**
17862  * @class Roo.bootstrap.MonthField
17863  * @extends Roo.bootstrap.Input
17864  * Bootstrap MonthField class
17865  * 
17866  * @cfg {String} language default en
17867  * 
17868  * @constructor
17869  * Create a new MonthField
17870  * @param {Object} config The config object
17871  */
17872
17873 Roo.bootstrap.MonthField = function(config){
17874     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17875     
17876     this.addEvents({
17877         /**
17878          * @event show
17879          * Fires when this field show.
17880          * @param {Roo.bootstrap.MonthField} this
17881          * @param {Mixed} date The date value
17882          */
17883         show : true,
17884         /**
17885          * @event show
17886          * Fires when this field hide.
17887          * @param {Roo.bootstrap.MonthField} this
17888          * @param {Mixed} date The date value
17889          */
17890         hide : true,
17891         /**
17892          * @event select
17893          * Fires when select a date.
17894          * @param {Roo.bootstrap.MonthField} this
17895          * @param {String} oldvalue The old value
17896          * @param {String} newvalue The new value
17897          */
17898         select : true
17899     });
17900 };
17901
17902 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17903     
17904     onRender: function(ct, position)
17905     {
17906         
17907         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17908         
17909         this.language = this.language || 'en';
17910         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17911         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17912         
17913         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17914         this.isInline = false;
17915         this.isInput = true;
17916         this.component = this.el.select('.add-on', true).first() || false;
17917         this.component = (this.component && this.component.length === 0) ? false : this.component;
17918         this.hasInput = this.component && this.inputEL().length;
17919         
17920         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17921         
17922         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17923         
17924         this.picker().on('mousedown', this.onMousedown, this);
17925         this.picker().on('click', this.onClick, this);
17926         
17927         this.picker().addClass('datepicker-dropdown');
17928         
17929         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17930             v.setStyle('width', '189px');
17931         });
17932         
17933         this.fillMonths();
17934         
17935         this.update();
17936         
17937         if(this.isInline) {
17938             this.show();
17939         }
17940         
17941     },
17942     
17943     setValue: function(v, suppressEvent)
17944     {   
17945         var o = this.getValue();
17946         
17947         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17948         
17949         this.update();
17950
17951         if(suppressEvent !== true){
17952             this.fireEvent('select', this, o, v);
17953         }
17954         
17955     },
17956     
17957     getValue: function()
17958     {
17959         return this.value;
17960     },
17961     
17962     onClick: function(e) 
17963     {
17964         e.stopPropagation();
17965         e.preventDefault();
17966         
17967         var target = e.getTarget();
17968         
17969         if(target.nodeName.toLowerCase() === 'i'){
17970             target = Roo.get(target).dom.parentNode;
17971         }
17972         
17973         var nodeName = target.nodeName;
17974         var className = target.className;
17975         var html = target.innerHTML;
17976         
17977         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17978             return;
17979         }
17980         
17981         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17982         
17983         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17984         
17985         this.hide();
17986                         
17987     },
17988     
17989     picker : function()
17990     {
17991         return this.pickerEl;
17992     },
17993     
17994     fillMonths: function()
17995     {    
17996         var i = 0;
17997         var months = this.picker().select('>.datepicker-months td', true).first();
17998         
17999         months.dom.innerHTML = '';
18000         
18001         while (i < 12) {
18002             var month = {
18003                 tag: 'span',
18004                 cls: 'month',
18005                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18006             };
18007             
18008             months.createChild(month);
18009         }
18010         
18011     },
18012     
18013     update: function()
18014     {
18015         var _this = this;
18016         
18017         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18018             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18019         }
18020         
18021         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18022             e.removeClass('active');
18023             
18024             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18025                 e.addClass('active');
18026             }
18027         })
18028     },
18029     
18030     place: function()
18031     {
18032         if(this.isInline) return;
18033         
18034         this.picker().removeClass(['bottom', 'top']);
18035         
18036         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18037             /*
18038              * place to the top of element!
18039              *
18040              */
18041             
18042             this.picker().addClass('top');
18043             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18044             
18045             return;
18046         }
18047         
18048         this.picker().addClass('bottom');
18049         
18050         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18051     },
18052     
18053     onFocus : function()
18054     {
18055         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18056         this.show();
18057     },
18058     
18059     onBlur : function()
18060     {
18061         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18062         
18063         var d = this.inputEl().getValue();
18064         
18065         this.setValue(d);
18066                 
18067         this.hide();
18068     },
18069     
18070     show : function()
18071     {
18072         this.picker().show();
18073         this.picker().select('>.datepicker-months', true).first().show();
18074         this.update();
18075         this.place();
18076         
18077         this.fireEvent('show', this, this.date);
18078     },
18079     
18080     hide : function()
18081     {
18082         if(this.isInline) return;
18083         this.picker().hide();
18084         this.fireEvent('hide', this, this.date);
18085         
18086     },
18087     
18088     onMousedown: function(e)
18089     {
18090         e.stopPropagation();
18091         e.preventDefault();
18092     },
18093     
18094     keyup: function(e)
18095     {
18096         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18097         this.update();
18098     },
18099
18100     fireKey: function(e)
18101     {
18102         if (!this.picker().isVisible()){
18103             if (e.keyCode == 27) // allow escape to hide and re-show picker
18104                 this.show();
18105             return;
18106         }
18107         
18108         var dir;
18109         
18110         switch(e.keyCode){
18111             case 27: // escape
18112                 this.hide();
18113                 e.preventDefault();
18114                 break;
18115             case 37: // left
18116             case 39: // right
18117                 dir = e.keyCode == 37 ? -1 : 1;
18118                 
18119                 this.vIndex = this.vIndex + dir;
18120                 
18121                 if(this.vIndex < 0){
18122                     this.vIndex = 0;
18123                 }
18124                 
18125                 if(this.vIndex > 11){
18126                     this.vIndex = 11;
18127                 }
18128                 
18129                 if(isNaN(this.vIndex)){
18130                     this.vIndex = 0;
18131                 }
18132                 
18133                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18134                 
18135                 break;
18136             case 38: // up
18137             case 40: // down
18138                 
18139                 dir = e.keyCode == 38 ? -1 : 1;
18140                 
18141                 this.vIndex = this.vIndex + dir * 4;
18142                 
18143                 if(this.vIndex < 0){
18144                     this.vIndex = 0;
18145                 }
18146                 
18147                 if(this.vIndex > 11){
18148                     this.vIndex = 11;
18149                 }
18150                 
18151                 if(isNaN(this.vIndex)){
18152                     this.vIndex = 0;
18153                 }
18154                 
18155                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18156                 break;
18157                 
18158             case 13: // enter
18159                 
18160                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18161                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18162                 }
18163                 
18164                 this.hide();
18165                 e.preventDefault();
18166                 break;
18167             case 9: // tab
18168                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18169                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18170                 }
18171                 this.hide();
18172                 break;
18173             case 16: // shift
18174             case 17: // ctrl
18175             case 18: // alt
18176                 break;
18177             default :
18178                 this.hide();
18179                 
18180         }
18181     },
18182     
18183     remove: function() 
18184     {
18185         this.picker().remove();
18186     }
18187    
18188 });
18189
18190 Roo.apply(Roo.bootstrap.MonthField,  {
18191     
18192     content : {
18193         tag: 'tbody',
18194         cn: [
18195         {
18196             tag: 'tr',
18197             cn: [
18198             {
18199                 tag: 'td',
18200                 colspan: '7'
18201             }
18202             ]
18203         }
18204         ]
18205     },
18206     
18207     dates:{
18208         en: {
18209             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18210             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18211         }
18212     }
18213 });
18214
18215 Roo.apply(Roo.bootstrap.MonthField,  {
18216   
18217     template : {
18218         tag: 'div',
18219         cls: 'datepicker dropdown-menu roo-dynamic',
18220         cn: [
18221             {
18222                 tag: 'div',
18223                 cls: 'datepicker-months',
18224                 cn: [
18225                 {
18226                     tag: 'table',
18227                     cls: 'table-condensed',
18228                     cn:[
18229                         Roo.bootstrap.DateField.content
18230                     ]
18231                 }
18232                 ]
18233             }
18234         ]
18235     }
18236 });
18237
18238  
18239
18240  
18241  /*
18242  * - LGPL
18243  *
18244  * CheckBox
18245  * 
18246  */
18247
18248 /**
18249  * @class Roo.bootstrap.CheckBox
18250  * @extends Roo.bootstrap.Input
18251  * Bootstrap CheckBox class
18252  * 
18253  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18254  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18255  * @cfg {String} boxLabel The text that appears beside the checkbox
18256  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18257  * @cfg {Boolean} checked initnal the element
18258  * @cfg {Boolean} inline inline the element (default false)
18259  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18260  * 
18261  * @constructor
18262  * Create a new CheckBox
18263  * @param {Object} config The config object
18264  */
18265
18266 Roo.bootstrap.CheckBox = function(config){
18267     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18268    
18269     this.addEvents({
18270         /**
18271         * @event check
18272         * Fires when the element is checked or unchecked.
18273         * @param {Roo.bootstrap.CheckBox} this This input
18274         * @param {Boolean} checked The new checked value
18275         */
18276        check : true
18277     });
18278     
18279 };
18280
18281 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18282   
18283     inputType: 'checkbox',
18284     inputValue: 1,
18285     valueOff: 0,
18286     boxLabel: false,
18287     checked: false,
18288     weight : false,
18289     inline: false,
18290     
18291     getAutoCreate : function()
18292     {
18293         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18294         
18295         var id = Roo.id();
18296         
18297         var cfg = {};
18298         
18299         cfg.cls = 'form-group ' + this.inputType; //input-group
18300         
18301         if(this.inline){
18302             cfg.cls += ' ' + this.inputType + '-inline';
18303         }
18304         
18305         var input =  {
18306             tag: 'input',
18307             id : id,
18308             type : this.inputType,
18309             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18310             cls : 'roo-' + this.inputType, //'form-box',
18311             placeholder : this.placeholder || ''
18312             
18313         };
18314         
18315         if (this.weight) { // Validity check?
18316             cfg.cls += " " + this.inputType + "-" + this.weight;
18317         }
18318         
18319         if (this.disabled) {
18320             input.disabled=true;
18321         }
18322         
18323         if(this.checked){
18324             input.checked = this.checked;
18325         }
18326         
18327         if (this.name) {
18328             input.name = this.name;
18329         }
18330         
18331         if (this.size) {
18332             input.cls += ' input-' + this.size;
18333         }
18334         
18335         var settings=this;
18336         
18337         ['xs','sm','md','lg'].map(function(size){
18338             if (settings[size]) {
18339                 cfg.cls += ' col-' + size + '-' + settings[size];
18340             }
18341         });
18342         
18343         var inputblock = input;
18344          
18345         if (this.before || this.after) {
18346             
18347             inputblock = {
18348                 cls : 'input-group',
18349                 cn :  [] 
18350             };
18351             
18352             if (this.before) {
18353                 inputblock.cn.push({
18354                     tag :'span',
18355                     cls : 'input-group-addon',
18356                     html : this.before
18357                 });
18358             }
18359             
18360             inputblock.cn.push(input);
18361             
18362             if (this.after) {
18363                 inputblock.cn.push({
18364                     tag :'span',
18365                     cls : 'input-group-addon',
18366                     html : this.after
18367                 });
18368             }
18369             
18370         }
18371         
18372         if (align ==='left' && this.fieldLabel.length) {
18373                 Roo.log("left and has label");
18374                 cfg.cn = [
18375                     
18376                     {
18377                         tag: 'label',
18378                         'for' :  id,
18379                         cls : 'control-label col-md-' + this.labelWidth,
18380                         html : this.fieldLabel
18381                         
18382                     },
18383                     {
18384                         cls : "col-md-" + (12 - this.labelWidth), 
18385                         cn: [
18386                             inputblock
18387                         ]
18388                     }
18389                     
18390                 ];
18391         } else if ( this.fieldLabel.length) {
18392                 Roo.log(" label");
18393                 cfg.cn = [
18394                    
18395                     {
18396                         tag: this.boxLabel ? 'span' : 'label',
18397                         'for': id,
18398                         cls: 'control-label box-input-label',
18399                         //cls : 'input-group-addon',
18400                         html : this.fieldLabel
18401                         
18402                     },
18403                     
18404                     inputblock
18405                     
18406                 ];
18407
18408         } else {
18409             
18410                 Roo.log(" no label && no align");
18411                 cfg.cn = [  inputblock ] ;
18412                 
18413                 
18414         }
18415         if(this.boxLabel){
18416              var boxLabelCfg = {
18417                 tag: 'label',
18418                 //'for': id, // box label is handled by onclick - so no for...
18419                 cls: 'box-label',
18420                 html: this.boxLabel
18421             };
18422             
18423             if(this.tooltip){
18424                 boxLabelCfg.tooltip = this.tooltip;
18425             }
18426              
18427             cfg.cn.push(boxLabelCfg);
18428         }
18429         
18430         
18431        
18432         return cfg;
18433         
18434     },
18435     
18436     /**
18437      * return the real input element.
18438      */
18439     inputEl: function ()
18440     {
18441         return this.el.select('input.roo-' + this.inputType,true).first();
18442     },
18443     
18444     labelEl: function()
18445     {
18446         return this.el.select('label.control-label',true).first();
18447     },
18448     /* depricated... */
18449     
18450     label: function()
18451     {
18452         return this.labelEl();
18453     },
18454     
18455     initEvents : function()
18456     {
18457 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18458         
18459         this.inputEl().on('click', this.onClick,  this);
18460         
18461         if (this.boxLabel) { 
18462             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18463         }
18464         
18465         this.startValue = this.getValue();
18466         
18467         if(this.groupId){
18468             Roo.bootstrap.CheckBox.register(this);
18469         }
18470     },
18471     
18472     onClick : function()
18473     {   
18474         this.setChecked(!this.checked);
18475     },
18476     
18477     setChecked : function(state,suppressEvent)
18478     {
18479         this.startValue = this.getValue();
18480         
18481         if(this.inputType == 'radio'){
18482             
18483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18484                 e.dom.checked = false;
18485             });
18486             
18487             this.inputEl().dom.checked = true;
18488             
18489             this.inputEl().dom.value = this.inputValue;
18490             
18491             if(suppressEvent !== true){
18492                 this.fireEvent('check', this, true);
18493             }
18494             
18495             this.validate();
18496             
18497             return;
18498         }
18499         
18500         this.checked = state;
18501         
18502         this.inputEl().dom.checked = state;
18503         
18504         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18505         
18506         if(suppressEvent !== true){
18507             this.fireEvent('check', this, state);
18508         }
18509         
18510         this.validate();
18511     },
18512     
18513     getValue : function()
18514     {
18515         if(this.inputType == 'radio'){
18516             return this.getGroupValue();
18517         }
18518         
18519         return this.inputEl().getValue();
18520         
18521     },
18522     
18523     getGroupValue : function()
18524     {
18525         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18526             return '';
18527         }
18528         
18529         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18530     },
18531     
18532     setValue : function(v,suppressEvent)
18533     {
18534         if(this.inputType == 'radio'){
18535             this.setGroupValue(v, suppressEvent);
18536             return;
18537         }
18538         
18539         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18540         
18541         this.validate();
18542     },
18543     
18544     setGroupValue : function(v, suppressEvent)
18545     {
18546         this.startValue = this.getValue();
18547         
18548         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18549             e.dom.checked = false;
18550             
18551             if(e.dom.value == v){
18552                 e.dom.checked = true;
18553             }
18554         });
18555         
18556         if(suppressEvent !== true){
18557             this.fireEvent('check', this, true);
18558         }
18559
18560         this.validate();
18561         
18562         return;
18563     },
18564     
18565     validate : function()
18566     {
18567         if(
18568                 this.disabled || 
18569                 (this.inputType == 'radio' && this.validateRadio()) ||
18570                 (this.inputType == 'checkbox' && this.validateCheckbox())
18571         ){
18572             this.markValid();
18573             return true;
18574         }
18575         
18576         this.markInvalid();
18577         return false;
18578     },
18579     
18580     validateRadio : function()
18581     {
18582         var valid = false;
18583         
18584         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18585             if(!e.dom.checked){
18586                 return;
18587             }
18588             
18589             valid = true;
18590             
18591             return false;
18592         });
18593         
18594         return valid;
18595     },
18596     
18597     validateCheckbox : function()
18598     {
18599         if(!this.groupId){
18600             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18601         }
18602         
18603         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18604         
18605         if(!group){
18606             return false;
18607         }
18608         
18609         var r = false;
18610         
18611         for(var i in group){
18612             if(r){
18613                 break;
18614             }
18615             
18616             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18617         }
18618         
18619         return r;
18620     },
18621     
18622     /**
18623      * Mark this field as valid
18624      */
18625     markValid : function()
18626     {
18627         if(this.allowBlank){
18628             return;
18629         }
18630         
18631         var _this = this;
18632         
18633         this.fireEvent('valid', this);
18634         
18635         if(this.inputType == 'radio'){
18636             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18637                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18638                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18639             });
18640             
18641             return;
18642         }
18643         
18644         if(!this.groupId){
18645             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18646             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18647             return;
18648         }
18649         
18650         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18651             
18652         if(!group){
18653             return;
18654         }
18655         
18656         for(var i in group){
18657             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18658             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18659         }
18660     },
18661     
18662      /**
18663      * Mark this field as invalid
18664      * @param {String} msg The validation message
18665      */
18666     markInvalid : function(msg)
18667     {
18668         if(this.allowBlank){
18669             return;
18670         }
18671         
18672         var _this = this;
18673         
18674         this.fireEvent('invalid', this, msg);
18675         
18676         if(this.inputType == 'radio'){
18677             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18678                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18679                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18680             });
18681             
18682             return;
18683         }
18684         
18685         if(!this.groupId){
18686             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18687             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18688             return;
18689         }
18690         
18691         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18692             
18693         if(!group){
18694             return;
18695         }
18696         
18697         for(var i in group){
18698             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18699             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18700         }
18701         
18702     }
18703     
18704 });
18705
18706 Roo.apply(Roo.bootstrap.CheckBox, {
18707     
18708     groups: {},
18709     
18710      /**
18711     * register a CheckBox Group
18712     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18713     */
18714     register : function(checkbox)
18715     {
18716         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18717             this.groups[checkbox.groupId] = {};
18718         }
18719         
18720         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18721             return;
18722         }
18723         
18724         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18725         
18726     },
18727     /**
18728     * fetch a CheckBox Group based on the group ID
18729     * @param {string} the group ID
18730     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18731     */
18732     get: function(groupId) {
18733         if (typeof(this.groups[groupId]) == 'undefined') {
18734             return false;
18735         }
18736         
18737         return this.groups[groupId] ;
18738     }
18739     
18740     
18741 });
18742 /*
18743  * - LGPL
18744  *
18745  * Radio
18746  *
18747  *
18748  * not inline
18749  *<div class="radio">
18750   <label>
18751     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18752     Option one is this and that&mdash;be sure to include why it's great
18753   </label>
18754 </div>
18755  *
18756  *
18757  *inline
18758  *<span>
18759  *<label class="radio-inline">fieldLabel</label>
18760  *<label class="radio-inline">
18761   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18762 </label>
18763 <span>
18764  * 
18765  * 
18766  */
18767
18768 /**
18769  * @class Roo.bootstrap.Radio
18770  * @extends Roo.bootstrap.CheckBox
18771  * Bootstrap Radio class
18772
18773  * @constructor
18774  * Create a new Radio
18775  * @param {Object} config The config object
18776  */
18777
18778 Roo.bootstrap.Radio = function(config){
18779     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18780    
18781 };
18782
18783 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18784     
18785     inputType: 'radio',
18786     inputValue: '',
18787     valueOff: '',
18788     
18789     getAutoCreate : function()
18790     {
18791         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18792         align = align || 'left'; // default...
18793         
18794         
18795         
18796         var id = Roo.id();
18797         
18798         var cfg = {
18799                 tag : this.inline ? 'span' : 'div',
18800                 cls : '',
18801                 cn : []
18802         };
18803         
18804         var inline = this.inline ? ' radio-inline' : '';
18805         
18806         var lbl = {
18807                 tag: 'label' ,
18808                 // does not need for, as we wrap the input with it..
18809                 'for' : id,
18810                 cls : 'control-label box-label' + inline,
18811                 cn : []
18812         };
18813         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18814         
18815         var fieldLabel = {
18816             tag: 'label' ,
18817             //cls : 'control-label' + inline,
18818             html : this.fieldLabel,
18819             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18820         };
18821         
18822  
18823         
18824         
18825         var input =  {
18826             tag: 'input',
18827             id : id,
18828             type : this.inputType,
18829             //value : (!this.checked) ? this.valueOff : this.inputValue,
18830             value : this.inputValue,
18831             cls : 'roo-radio',
18832             placeholder : this.placeholder || '' // ?? needed????
18833             
18834         };
18835         if (this.weight) { // Validity check?
18836             input.cls += " radio-" + this.weight;
18837         }
18838         if (this.disabled) {
18839             input.disabled=true;
18840         }
18841         
18842         if(this.checked){
18843             input.checked = this.checked;
18844         }
18845         
18846         if (this.name) {
18847             input.name = this.name;
18848         }
18849         
18850         if (this.size) {
18851             input.cls += ' input-' + this.size;
18852         }
18853         
18854         //?? can span's inline have a width??
18855         
18856         var settings=this;
18857         ['xs','sm','md','lg'].map(function(size){
18858             if (settings[size]) {
18859                 cfg.cls += ' col-' + size + '-' + settings[size];
18860             }
18861         });
18862         
18863         var inputblock = input;
18864         
18865         if (this.before || this.after) {
18866             
18867             inputblock = {
18868                 cls : 'input-group',
18869                 tag : 'span',
18870                 cn :  [] 
18871             };
18872             if (this.before) {
18873                 inputblock.cn.push({
18874                     tag :'span',
18875                     cls : 'input-group-addon',
18876                     html : this.before
18877                 });
18878             }
18879             inputblock.cn.push(input);
18880             if (this.after) {
18881                 inputblock.cn.push({
18882                     tag :'span',
18883                     cls : 'input-group-addon',
18884                     html : this.after
18885                 });
18886             }
18887             
18888         };
18889         
18890         
18891         if (this.fieldLabel && this.fieldLabel.length) {
18892             cfg.cn.push(fieldLabel);
18893         }
18894        
18895         // normal bootstrap puts the input inside the label.
18896         // however with our styled version - it has to go after the input.
18897        
18898         //lbl.cn.push(inputblock);
18899         
18900         var lblwrap =  {
18901             tag: 'span',
18902             cls: 'radio' + inline,
18903             cn: [
18904                 inputblock,
18905                 lbl
18906             ]
18907         };
18908         
18909         cfg.cn.push( lblwrap);
18910         
18911         if(this.boxLabel){
18912             lbl.cn.push({
18913                 tag: 'span',
18914                 html: this.boxLabel
18915             })
18916         }
18917          
18918         
18919         return cfg;
18920         
18921     },
18922     
18923     initEvents : function()
18924     {
18925 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18926         
18927         this.inputEl().on('click', this.onClick,  this);
18928         if (this.boxLabel) {
18929             Roo.log('find label')
18930             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18931         }
18932         
18933     },
18934     
18935     inputEl: function ()
18936     {
18937         return this.el.select('input.roo-radio',true).first();
18938     },
18939     onClick : function()
18940     {   
18941         Roo.log("click");
18942         this.setChecked(true);
18943     },
18944     
18945     setChecked : function(state,suppressEvent)
18946     {
18947         if(state){
18948             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18949                 v.dom.checked = false;
18950             });
18951         }
18952         Roo.log(this.inputEl().dom);
18953         this.checked = state;
18954         this.inputEl().dom.checked = state;
18955         
18956         if(suppressEvent !== true){
18957             this.fireEvent('check', this, state);
18958         }
18959         
18960         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18961         
18962     },
18963     
18964     getGroupValue : function()
18965     {
18966         var value = '';
18967         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18968             if(v.dom.checked == true){
18969                 value = v.dom.value;
18970             }
18971         });
18972         
18973         return value;
18974     },
18975     
18976     /**
18977      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18978      * @return {Mixed} value The field value
18979      */
18980     getValue : function(){
18981         return this.getGroupValue();
18982     }
18983     
18984 });
18985
18986  
18987 //<script type="text/javascript">
18988
18989 /*
18990  * Based  Ext JS Library 1.1.1
18991  * Copyright(c) 2006-2007, Ext JS, LLC.
18992  * LGPL
18993  *
18994  */
18995  
18996 /**
18997  * @class Roo.HtmlEditorCore
18998  * @extends Roo.Component
18999  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19000  *
19001  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19002  */
19003
19004 Roo.HtmlEditorCore = function(config){
19005     
19006     
19007     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19008     
19009     
19010     this.addEvents({
19011         /**
19012          * @event initialize
19013          * Fires when the editor is fully initialized (including the iframe)
19014          * @param {Roo.HtmlEditorCore} this
19015          */
19016         initialize: true,
19017         /**
19018          * @event activate
19019          * Fires when the editor is first receives the focus. Any insertion must wait
19020          * until after this event.
19021          * @param {Roo.HtmlEditorCore} this
19022          */
19023         activate: true,
19024          /**
19025          * @event beforesync
19026          * Fires before the textarea is updated with content from the editor iframe. Return false
19027          * to cancel the sync.
19028          * @param {Roo.HtmlEditorCore} this
19029          * @param {String} html
19030          */
19031         beforesync: true,
19032          /**
19033          * @event beforepush
19034          * Fires before the iframe editor is updated with content from the textarea. Return false
19035          * to cancel the push.
19036          * @param {Roo.HtmlEditorCore} this
19037          * @param {String} html
19038          */
19039         beforepush: true,
19040          /**
19041          * @event sync
19042          * Fires when the textarea is updated with content from the editor iframe.
19043          * @param {Roo.HtmlEditorCore} this
19044          * @param {String} html
19045          */
19046         sync: true,
19047          /**
19048          * @event push
19049          * Fires when the iframe editor is updated with content from the textarea.
19050          * @param {Roo.HtmlEditorCore} this
19051          * @param {String} html
19052          */
19053         push: true,
19054         
19055         /**
19056          * @event editorevent
19057          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19058          * @param {Roo.HtmlEditorCore} this
19059          */
19060         editorevent: true
19061         
19062     });
19063     
19064     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19065     
19066     // defaults : white / black...
19067     this.applyBlacklists();
19068     
19069     
19070     
19071 };
19072
19073
19074 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19075
19076
19077      /**
19078      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19079      */
19080     
19081     owner : false,
19082     
19083      /**
19084      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19085      *                        Roo.resizable.
19086      */
19087     resizable : false,
19088      /**
19089      * @cfg {Number} height (in pixels)
19090      */   
19091     height: 300,
19092    /**
19093      * @cfg {Number} width (in pixels)
19094      */   
19095     width: 500,
19096     
19097     /**
19098      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19099      * 
19100      */
19101     stylesheets: false,
19102     
19103     // id of frame..
19104     frameId: false,
19105     
19106     // private properties
19107     validationEvent : false,
19108     deferHeight: true,
19109     initialized : false,
19110     activated : false,
19111     sourceEditMode : false,
19112     onFocus : Roo.emptyFn,
19113     iframePad:3,
19114     hideMode:'offsets',
19115     
19116     clearUp: true,
19117     
19118     // blacklist + whitelisted elements..
19119     black: false,
19120     white: false,
19121      
19122     
19123
19124     /**
19125      * Protected method that will not generally be called directly. It
19126      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19127      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19128      */
19129     getDocMarkup : function(){
19130         // body styles..
19131         var st = '';
19132         
19133         // inherit styels from page...?? 
19134         if (this.stylesheets === false) {
19135             
19136             Roo.get(document.head).select('style').each(function(node) {
19137                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19138             });
19139             
19140             Roo.get(document.head).select('link').each(function(node) { 
19141                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19142             });
19143             
19144         } else if (!this.stylesheets.length) {
19145                 // simple..
19146                 st = '<style type="text/css">' +
19147                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19148                    '</style>';
19149         } else { 
19150             
19151         }
19152         
19153         st +=  '<style type="text/css">' +
19154             'IMG { cursor: pointer } ' +
19155         '</style>';
19156
19157         
19158         return '<html><head>' + st  +
19159             //<style type="text/css">' +
19160             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19161             //'</style>' +
19162             ' </head><body class="roo-htmleditor-body"></body></html>';
19163     },
19164
19165     // private
19166     onRender : function(ct, position)
19167     {
19168         var _t = this;
19169         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19170         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19171         
19172         
19173         this.el.dom.style.border = '0 none';
19174         this.el.dom.setAttribute('tabIndex', -1);
19175         this.el.addClass('x-hidden hide');
19176         
19177         
19178         
19179         if(Roo.isIE){ // fix IE 1px bogus margin
19180             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19181         }
19182        
19183         
19184         this.frameId = Roo.id();
19185         
19186          
19187         
19188         var iframe = this.owner.wrap.createChild({
19189             tag: 'iframe',
19190             cls: 'form-control', // bootstrap..
19191             id: this.frameId,
19192             name: this.frameId,
19193             frameBorder : 'no',
19194             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19195         }, this.el
19196         );
19197         
19198         
19199         this.iframe = iframe.dom;
19200
19201          this.assignDocWin();
19202         
19203         this.doc.designMode = 'on';
19204        
19205         this.doc.open();
19206         this.doc.write(this.getDocMarkup());
19207         this.doc.close();
19208
19209         
19210         var task = { // must defer to wait for browser to be ready
19211             run : function(){
19212                 //console.log("run task?" + this.doc.readyState);
19213                 this.assignDocWin();
19214                 if(this.doc.body || this.doc.readyState == 'complete'){
19215                     try {
19216                         this.doc.designMode="on";
19217                     } catch (e) {
19218                         return;
19219                     }
19220                     Roo.TaskMgr.stop(task);
19221                     this.initEditor.defer(10, this);
19222                 }
19223             },
19224             interval : 10,
19225             duration: 10000,
19226             scope: this
19227         };
19228         Roo.TaskMgr.start(task);
19229
19230     },
19231
19232     // private
19233     onResize : function(w, h)
19234     {
19235          Roo.log('resize: ' +w + ',' + h );
19236         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19237         if(!this.iframe){
19238             return;
19239         }
19240         if(typeof w == 'number'){
19241             
19242             this.iframe.style.width = w + 'px';
19243         }
19244         if(typeof h == 'number'){
19245             
19246             this.iframe.style.height = h + 'px';
19247             if(this.doc){
19248                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19249             }
19250         }
19251         
19252     },
19253
19254     /**
19255      * Toggles the editor between standard and source edit mode.
19256      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19257      */
19258     toggleSourceEdit : function(sourceEditMode){
19259         
19260         this.sourceEditMode = sourceEditMode === true;
19261         
19262         if(this.sourceEditMode){
19263  
19264             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19265             
19266         }else{
19267             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19268             //this.iframe.className = '';
19269             this.deferFocus();
19270         }
19271         //this.setSize(this.owner.wrap.getSize());
19272         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19273     },
19274
19275     
19276   
19277
19278     /**
19279      * Protected method that will not generally be called directly. If you need/want
19280      * custom HTML cleanup, this is the method you should override.
19281      * @param {String} html The HTML to be cleaned
19282      * return {String} The cleaned HTML
19283      */
19284     cleanHtml : function(html){
19285         html = String(html);
19286         if(html.length > 5){
19287             if(Roo.isSafari){ // strip safari nonsense
19288                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19289             }
19290         }
19291         if(html == '&nbsp;'){
19292             html = '';
19293         }
19294         return html;
19295     },
19296
19297     /**
19298      * HTML Editor -> Textarea
19299      * Protected method that will not generally be called directly. Syncs the contents
19300      * of the editor iframe with the textarea.
19301      */
19302     syncValue : function(){
19303         if(this.initialized){
19304             var bd = (this.doc.body || this.doc.documentElement);
19305             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19306             var html = bd.innerHTML;
19307             if(Roo.isSafari){
19308                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19309                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19310                 if(m && m[1]){
19311                     html = '<div style="'+m[0]+'">' + html + '</div>';
19312                 }
19313             }
19314             html = this.cleanHtml(html);
19315             // fix up the special chars.. normaly like back quotes in word...
19316             // however we do not want to do this with chinese..
19317             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19318                 var cc = b.charCodeAt();
19319                 if (
19320                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19321                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19322                     (cc >= 0xf900 && cc < 0xfb00 )
19323                 ) {
19324                         return b;
19325                 }
19326                 return "&#"+cc+";" 
19327             });
19328             if(this.owner.fireEvent('beforesync', this, html) !== false){
19329                 this.el.dom.value = html;
19330                 this.owner.fireEvent('sync', this, html);
19331             }
19332         }
19333     },
19334
19335     /**
19336      * Protected method that will not generally be called directly. Pushes the value of the textarea
19337      * into the iframe editor.
19338      */
19339     pushValue : function(){
19340         if(this.initialized){
19341             var v = this.el.dom.value.trim();
19342             
19343 //            if(v.length < 1){
19344 //                v = '&#160;';
19345 //            }
19346             
19347             if(this.owner.fireEvent('beforepush', this, v) !== false){
19348                 var d = (this.doc.body || this.doc.documentElement);
19349                 d.innerHTML = v;
19350                 this.cleanUpPaste();
19351                 this.el.dom.value = d.innerHTML;
19352                 this.owner.fireEvent('push', this, v);
19353             }
19354         }
19355     },
19356
19357     // private
19358     deferFocus : function(){
19359         this.focus.defer(10, this);
19360     },
19361
19362     // doc'ed in Field
19363     focus : function(){
19364         if(this.win && !this.sourceEditMode){
19365             this.win.focus();
19366         }else{
19367             this.el.focus();
19368         }
19369     },
19370     
19371     assignDocWin: function()
19372     {
19373         var iframe = this.iframe;
19374         
19375          if(Roo.isIE){
19376             this.doc = iframe.contentWindow.document;
19377             this.win = iframe.contentWindow;
19378         } else {
19379 //            if (!Roo.get(this.frameId)) {
19380 //                return;
19381 //            }
19382 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19383 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19384             
19385             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19386                 return;
19387             }
19388             
19389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19390             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19391         }
19392     },
19393     
19394     // private
19395     initEditor : function(){
19396         //console.log("INIT EDITOR");
19397         this.assignDocWin();
19398         
19399         
19400         
19401         this.doc.designMode="on";
19402         this.doc.open();
19403         this.doc.write(this.getDocMarkup());
19404         this.doc.close();
19405         
19406         var dbody = (this.doc.body || this.doc.documentElement);
19407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19408         // this copies styles from the containing element into thsi one..
19409         // not sure why we need all of this..
19410         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19411         
19412         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19413         //ss['background-attachment'] = 'fixed'; // w3c
19414         dbody.bgProperties = 'fixed'; // ie
19415         //Roo.DomHelper.applyStyles(dbody, ss);
19416         Roo.EventManager.on(this.doc, {
19417             //'mousedown': this.onEditorEvent,
19418             'mouseup': this.onEditorEvent,
19419             'dblclick': this.onEditorEvent,
19420             'click': this.onEditorEvent,
19421             'keyup': this.onEditorEvent,
19422             buffer:100,
19423             scope: this
19424         });
19425         if(Roo.isGecko){
19426             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19427         }
19428         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19429             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19430         }
19431         this.initialized = true;
19432
19433         this.owner.fireEvent('initialize', this);
19434         this.pushValue();
19435     },
19436
19437     // private
19438     onDestroy : function(){
19439         
19440         
19441         
19442         if(this.rendered){
19443             
19444             //for (var i =0; i < this.toolbars.length;i++) {
19445             //    // fixme - ask toolbars for heights?
19446             //    this.toolbars[i].onDestroy();
19447            // }
19448             
19449             //this.wrap.dom.innerHTML = '';
19450             //this.wrap.remove();
19451         }
19452     },
19453
19454     // private
19455     onFirstFocus : function(){
19456         
19457         this.assignDocWin();
19458         
19459         
19460         this.activated = true;
19461          
19462     
19463         if(Roo.isGecko){ // prevent silly gecko errors
19464             this.win.focus();
19465             var s = this.win.getSelection();
19466             if(!s.focusNode || s.focusNode.nodeType != 3){
19467                 var r = s.getRangeAt(0);
19468                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19469                 r.collapse(true);
19470                 this.deferFocus();
19471             }
19472             try{
19473                 this.execCmd('useCSS', true);
19474                 this.execCmd('styleWithCSS', false);
19475             }catch(e){}
19476         }
19477         this.owner.fireEvent('activate', this);
19478     },
19479
19480     // private
19481     adjustFont: function(btn){
19482         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19483         //if(Roo.isSafari){ // safari
19484         //    adjust *= 2;
19485        // }
19486         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19487         if(Roo.isSafari){ // safari
19488             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19489             v =  (v < 10) ? 10 : v;
19490             v =  (v > 48) ? 48 : v;
19491             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19492             
19493         }
19494         
19495         
19496         v = Math.max(1, v+adjust);
19497         
19498         this.execCmd('FontSize', v  );
19499     },
19500
19501     onEditorEvent : function(e)
19502     {
19503         this.owner.fireEvent('editorevent', this, e);
19504       //  this.updateToolbar();
19505         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19506     },
19507
19508     insertTag : function(tg)
19509     {
19510         // could be a bit smarter... -> wrap the current selected tRoo..
19511         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19512             
19513             range = this.createRange(this.getSelection());
19514             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19515             wrappingNode.appendChild(range.extractContents());
19516             range.insertNode(wrappingNode);
19517
19518             return;
19519             
19520             
19521             
19522         }
19523         this.execCmd("formatblock",   tg);
19524         
19525     },
19526     
19527     insertText : function(txt)
19528     {
19529         
19530         
19531         var range = this.createRange();
19532         range.deleteContents();
19533                //alert(Sender.getAttribute('label'));
19534                
19535         range.insertNode(this.doc.createTextNode(txt));
19536     } ,
19537     
19538      
19539
19540     /**
19541      * Executes a Midas editor command on the editor document and performs necessary focus and
19542      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19543      * @param {String} cmd The Midas command
19544      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19545      */
19546     relayCmd : function(cmd, value){
19547         this.win.focus();
19548         this.execCmd(cmd, value);
19549         this.owner.fireEvent('editorevent', this);
19550         //this.updateToolbar();
19551         this.owner.deferFocus();
19552     },
19553
19554     /**
19555      * Executes a Midas editor command directly on the editor document.
19556      * For visual commands, you should use {@link #relayCmd} instead.
19557      * <b>This should only be called after the editor is initialized.</b>
19558      * @param {String} cmd The Midas command
19559      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19560      */
19561     execCmd : function(cmd, value){
19562         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19563         this.syncValue();
19564     },
19565  
19566  
19567    
19568     /**
19569      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19570      * to insert tRoo.
19571      * @param {String} text | dom node.. 
19572      */
19573     insertAtCursor : function(text)
19574     {
19575         
19576         
19577         
19578         if(!this.activated){
19579             return;
19580         }
19581         /*
19582         if(Roo.isIE){
19583             this.win.focus();
19584             var r = this.doc.selection.createRange();
19585             if(r){
19586                 r.collapse(true);
19587                 r.pasteHTML(text);
19588                 this.syncValue();
19589                 this.deferFocus();
19590             
19591             }
19592             return;
19593         }
19594         */
19595         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19596             this.win.focus();
19597             
19598             
19599             // from jquery ui (MIT licenced)
19600             var range, node;
19601             var win = this.win;
19602             
19603             if (win.getSelection && win.getSelection().getRangeAt) {
19604                 range = win.getSelection().getRangeAt(0);
19605                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19606                 range.insertNode(node);
19607             } else if (win.document.selection && win.document.selection.createRange) {
19608                 // no firefox support
19609                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19610                 win.document.selection.createRange().pasteHTML(txt);
19611             } else {
19612                 // no firefox support
19613                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19614                 this.execCmd('InsertHTML', txt);
19615             } 
19616             
19617             this.syncValue();
19618             
19619             this.deferFocus();
19620         }
19621     },
19622  // private
19623     mozKeyPress : function(e){
19624         if(e.ctrlKey){
19625             var c = e.getCharCode(), cmd;
19626           
19627             if(c > 0){
19628                 c = String.fromCharCode(c).toLowerCase();
19629                 switch(c){
19630                     case 'b':
19631                         cmd = 'bold';
19632                         break;
19633                     case 'i':
19634                         cmd = 'italic';
19635                         break;
19636                     
19637                     case 'u':
19638                         cmd = 'underline';
19639                         break;
19640                     
19641                     case 'v':
19642                         this.cleanUpPaste.defer(100, this);
19643                         return;
19644                         
19645                 }
19646                 if(cmd){
19647                     this.win.focus();
19648                     this.execCmd(cmd);
19649                     this.deferFocus();
19650                     e.preventDefault();
19651                 }
19652                 
19653             }
19654         }
19655     },
19656
19657     // private
19658     fixKeys : function(){ // load time branching for fastest keydown performance
19659         if(Roo.isIE){
19660             return function(e){
19661                 var k = e.getKey(), r;
19662                 if(k == e.TAB){
19663                     e.stopEvent();
19664                     r = this.doc.selection.createRange();
19665                     if(r){
19666                         r.collapse(true);
19667                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19668                         this.deferFocus();
19669                     }
19670                     return;
19671                 }
19672                 
19673                 if(k == e.ENTER){
19674                     r = this.doc.selection.createRange();
19675                     if(r){
19676                         var target = r.parentElement();
19677                         if(!target || target.tagName.toLowerCase() != 'li'){
19678                             e.stopEvent();
19679                             r.pasteHTML('<br />');
19680                             r.collapse(false);
19681                             r.select();
19682                         }
19683                     }
19684                 }
19685                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19686                     this.cleanUpPaste.defer(100, this);
19687                     return;
19688                 }
19689                 
19690                 
19691             };
19692         }else if(Roo.isOpera){
19693             return function(e){
19694                 var k = e.getKey();
19695                 if(k == e.TAB){
19696                     e.stopEvent();
19697                     this.win.focus();
19698                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19699                     this.deferFocus();
19700                 }
19701                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19702                     this.cleanUpPaste.defer(100, this);
19703                     return;
19704                 }
19705                 
19706             };
19707         }else if(Roo.isSafari){
19708             return function(e){
19709                 var k = e.getKey();
19710                 
19711                 if(k == e.TAB){
19712                     e.stopEvent();
19713                     this.execCmd('InsertText','\t');
19714                     this.deferFocus();
19715                     return;
19716                 }
19717                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19718                     this.cleanUpPaste.defer(100, this);
19719                     return;
19720                 }
19721                 
19722              };
19723         }
19724     }(),
19725     
19726     getAllAncestors: function()
19727     {
19728         var p = this.getSelectedNode();
19729         var a = [];
19730         if (!p) {
19731             a.push(p); // push blank onto stack..
19732             p = this.getParentElement();
19733         }
19734         
19735         
19736         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19737             a.push(p);
19738             p = p.parentNode;
19739         }
19740         a.push(this.doc.body);
19741         return a;
19742     },
19743     lastSel : false,
19744     lastSelNode : false,
19745     
19746     
19747     getSelection : function() 
19748     {
19749         this.assignDocWin();
19750         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19751     },
19752     
19753     getSelectedNode: function() 
19754     {
19755         // this may only work on Gecko!!!
19756         
19757         // should we cache this!!!!
19758         
19759         
19760         
19761          
19762         var range = this.createRange(this.getSelection()).cloneRange();
19763         
19764         if (Roo.isIE) {
19765             var parent = range.parentElement();
19766             while (true) {
19767                 var testRange = range.duplicate();
19768                 testRange.moveToElementText(parent);
19769                 if (testRange.inRange(range)) {
19770                     break;
19771                 }
19772                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19773                     break;
19774                 }
19775                 parent = parent.parentElement;
19776             }
19777             return parent;
19778         }
19779         
19780         // is ancestor a text element.
19781         var ac =  range.commonAncestorContainer;
19782         if (ac.nodeType == 3) {
19783             ac = ac.parentNode;
19784         }
19785         
19786         var ar = ac.childNodes;
19787          
19788         var nodes = [];
19789         var other_nodes = [];
19790         var has_other_nodes = false;
19791         for (var i=0;i<ar.length;i++) {
19792             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19793                 continue;
19794             }
19795             // fullly contained node.
19796             
19797             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19798                 nodes.push(ar[i]);
19799                 continue;
19800             }
19801             
19802             // probably selected..
19803             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19804                 other_nodes.push(ar[i]);
19805                 continue;
19806             }
19807             // outer..
19808             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19809                 continue;
19810             }
19811             
19812             
19813             has_other_nodes = true;
19814         }
19815         if (!nodes.length && other_nodes.length) {
19816             nodes= other_nodes;
19817         }
19818         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19819             return false;
19820         }
19821         
19822         return nodes[0];
19823     },
19824     createRange: function(sel)
19825     {
19826         // this has strange effects when using with 
19827         // top toolbar - not sure if it's a great idea.
19828         //this.editor.contentWindow.focus();
19829         if (typeof sel != "undefined") {
19830             try {
19831                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19832             } catch(e) {
19833                 return this.doc.createRange();
19834             }
19835         } else {
19836             return this.doc.createRange();
19837         }
19838     },
19839     getParentElement: function()
19840     {
19841         
19842         this.assignDocWin();
19843         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19844         
19845         var range = this.createRange(sel);
19846          
19847         try {
19848             var p = range.commonAncestorContainer;
19849             while (p.nodeType == 3) { // text node
19850                 p = p.parentNode;
19851             }
19852             return p;
19853         } catch (e) {
19854             return null;
19855         }
19856     
19857     },
19858     /***
19859      *
19860      * Range intersection.. the hard stuff...
19861      *  '-1' = before
19862      *  '0' = hits..
19863      *  '1' = after.
19864      *         [ -- selected range --- ]
19865      *   [fail]                        [fail]
19866      *
19867      *    basically..
19868      *      if end is before start or  hits it. fail.
19869      *      if start is after end or hits it fail.
19870      *
19871      *   if either hits (but other is outside. - then it's not 
19872      *   
19873      *    
19874      **/
19875     
19876     
19877     // @see http://www.thismuchiknow.co.uk/?p=64.
19878     rangeIntersectsNode : function(range, node)
19879     {
19880         var nodeRange = node.ownerDocument.createRange();
19881         try {
19882             nodeRange.selectNode(node);
19883         } catch (e) {
19884             nodeRange.selectNodeContents(node);
19885         }
19886     
19887         var rangeStartRange = range.cloneRange();
19888         rangeStartRange.collapse(true);
19889     
19890         var rangeEndRange = range.cloneRange();
19891         rangeEndRange.collapse(false);
19892     
19893         var nodeStartRange = nodeRange.cloneRange();
19894         nodeStartRange.collapse(true);
19895     
19896         var nodeEndRange = nodeRange.cloneRange();
19897         nodeEndRange.collapse(false);
19898     
19899         return rangeStartRange.compareBoundaryPoints(
19900                  Range.START_TO_START, nodeEndRange) == -1 &&
19901                rangeEndRange.compareBoundaryPoints(
19902                  Range.START_TO_START, nodeStartRange) == 1;
19903         
19904          
19905     },
19906     rangeCompareNode : function(range, node)
19907     {
19908         var nodeRange = node.ownerDocument.createRange();
19909         try {
19910             nodeRange.selectNode(node);
19911         } catch (e) {
19912             nodeRange.selectNodeContents(node);
19913         }
19914         
19915         
19916         range.collapse(true);
19917     
19918         nodeRange.collapse(true);
19919      
19920         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19921         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19922          
19923         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19924         
19925         var nodeIsBefore   =  ss == 1;
19926         var nodeIsAfter    = ee == -1;
19927         
19928         if (nodeIsBefore && nodeIsAfter)
19929             return 0; // outer
19930         if (!nodeIsBefore && nodeIsAfter)
19931             return 1; //right trailed.
19932         
19933         if (nodeIsBefore && !nodeIsAfter)
19934             return 2;  // left trailed.
19935         // fully contined.
19936         return 3;
19937     },
19938
19939     // private? - in a new class?
19940     cleanUpPaste :  function()
19941     {
19942         // cleans up the whole document..
19943         Roo.log('cleanuppaste');
19944         
19945         this.cleanUpChildren(this.doc.body);
19946         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19947         if (clean != this.doc.body.innerHTML) {
19948             this.doc.body.innerHTML = clean;
19949         }
19950         
19951     },
19952     
19953     cleanWordChars : function(input) {// change the chars to hex code
19954         var he = Roo.HtmlEditorCore;
19955         
19956         var output = input;
19957         Roo.each(he.swapCodes, function(sw) { 
19958             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19959             
19960             output = output.replace(swapper, sw[1]);
19961         });
19962         
19963         return output;
19964     },
19965     
19966     
19967     cleanUpChildren : function (n)
19968     {
19969         if (!n.childNodes.length) {
19970             return;
19971         }
19972         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19973            this.cleanUpChild(n.childNodes[i]);
19974         }
19975     },
19976     
19977     
19978         
19979     
19980     cleanUpChild : function (node)
19981     {
19982         var ed = this;
19983         //console.log(node);
19984         if (node.nodeName == "#text") {
19985             // clean up silly Windows -- stuff?
19986             return; 
19987         }
19988         if (node.nodeName == "#comment") {
19989             node.parentNode.removeChild(node);
19990             // clean up silly Windows -- stuff?
19991             return; 
19992         }
19993         var lcname = node.tagName.toLowerCase();
19994         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19995         // whitelist of tags..
19996         
19997         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19998             // remove node.
19999             node.parentNode.removeChild(node);
20000             return;
20001             
20002         }
20003         
20004         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20005         
20006         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20007         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20008         
20009         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20010         //    remove_keep_children = true;
20011         //}
20012         
20013         if (remove_keep_children) {
20014             this.cleanUpChildren(node);
20015             // inserts everything just before this node...
20016             while (node.childNodes.length) {
20017                 var cn = node.childNodes[0];
20018                 node.removeChild(cn);
20019                 node.parentNode.insertBefore(cn, node);
20020             }
20021             node.parentNode.removeChild(node);
20022             return;
20023         }
20024         
20025         if (!node.attributes || !node.attributes.length) {
20026             this.cleanUpChildren(node);
20027             return;
20028         }
20029         
20030         function cleanAttr(n,v)
20031         {
20032             
20033             if (v.match(/^\./) || v.match(/^\//)) {
20034                 return;
20035             }
20036             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20037                 return;
20038             }
20039             if (v.match(/^#/)) {
20040                 return;
20041             }
20042 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20043             node.removeAttribute(n);
20044             
20045         }
20046         
20047         var cwhite = this.cwhite;
20048         var cblack = this.cblack;
20049             
20050         function cleanStyle(n,v)
20051         {
20052             if (v.match(/expression/)) { //XSS?? should we even bother..
20053                 node.removeAttribute(n);
20054                 return;
20055             }
20056             
20057             var parts = v.split(/;/);
20058             var clean = [];
20059             
20060             Roo.each(parts, function(p) {
20061                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20062                 if (!p.length) {
20063                     return true;
20064                 }
20065                 var l = p.split(':').shift().replace(/\s+/g,'');
20066                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20067                 
20068                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20069 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20070                     //node.removeAttribute(n);
20071                     return true;
20072                 }
20073                 //Roo.log()
20074                 // only allow 'c whitelisted system attributes'
20075                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20076 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20077                     //node.removeAttribute(n);
20078                     return true;
20079                 }
20080                 
20081                 
20082                  
20083                 
20084                 clean.push(p);
20085                 return true;
20086             });
20087             if (clean.length) { 
20088                 node.setAttribute(n, clean.join(';'));
20089             } else {
20090                 node.removeAttribute(n);
20091             }
20092             
20093         }
20094         
20095         
20096         for (var i = node.attributes.length-1; i > -1 ; i--) {
20097             var a = node.attributes[i];
20098             //console.log(a);
20099             
20100             if (a.name.toLowerCase().substr(0,2)=='on')  {
20101                 node.removeAttribute(a.name);
20102                 continue;
20103             }
20104             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20105                 node.removeAttribute(a.name);
20106                 continue;
20107             }
20108             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20109                 cleanAttr(a.name,a.value); // fixme..
20110                 continue;
20111             }
20112             if (a.name == 'style') {
20113                 cleanStyle(a.name,a.value);
20114                 continue;
20115             }
20116             /// clean up MS crap..
20117             // tecnically this should be a list of valid class'es..
20118             
20119             
20120             if (a.name == 'class') {
20121                 if (a.value.match(/^Mso/)) {
20122                     node.className = '';
20123                 }
20124                 
20125                 if (a.value.match(/body/)) {
20126                     node.className = '';
20127                 }
20128                 continue;
20129             }
20130             
20131             // style cleanup!?
20132             // class cleanup?
20133             
20134         }
20135         
20136         
20137         this.cleanUpChildren(node);
20138         
20139         
20140     },
20141     
20142     /**
20143      * Clean up MS wordisms...
20144      */
20145     cleanWord : function(node)
20146     {
20147         
20148         
20149         if (!node) {
20150             this.cleanWord(this.doc.body);
20151             return;
20152         }
20153         if (node.nodeName == "#text") {
20154             // clean up silly Windows -- stuff?
20155             return; 
20156         }
20157         if (node.nodeName == "#comment") {
20158             node.parentNode.removeChild(node);
20159             // clean up silly Windows -- stuff?
20160             return; 
20161         }
20162         
20163         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20164             node.parentNode.removeChild(node);
20165             return;
20166         }
20167         
20168         // remove - but keep children..
20169         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20170             while (node.childNodes.length) {
20171                 var cn = node.childNodes[0];
20172                 node.removeChild(cn);
20173                 node.parentNode.insertBefore(cn, node);
20174             }
20175             node.parentNode.removeChild(node);
20176             this.iterateChildren(node, this.cleanWord);
20177             return;
20178         }
20179         // clean styles
20180         if (node.className.length) {
20181             
20182             var cn = node.className.split(/\W+/);
20183             var cna = [];
20184             Roo.each(cn, function(cls) {
20185                 if (cls.match(/Mso[a-zA-Z]+/)) {
20186                     return;
20187                 }
20188                 cna.push(cls);
20189             });
20190             node.className = cna.length ? cna.join(' ') : '';
20191             if (!cna.length) {
20192                 node.removeAttribute("class");
20193             }
20194         }
20195         
20196         if (node.hasAttribute("lang")) {
20197             node.removeAttribute("lang");
20198         }
20199         
20200         if (node.hasAttribute("style")) {
20201             
20202             var styles = node.getAttribute("style").split(";");
20203             var nstyle = [];
20204             Roo.each(styles, function(s) {
20205                 if (!s.match(/:/)) {
20206                     return;
20207                 }
20208                 var kv = s.split(":");
20209                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20210                     return;
20211                 }
20212                 // what ever is left... we allow.
20213                 nstyle.push(s);
20214             });
20215             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20216             if (!nstyle.length) {
20217                 node.removeAttribute('style');
20218             }
20219         }
20220         this.iterateChildren(node, this.cleanWord);
20221         
20222         
20223         
20224     },
20225     /**
20226      * iterateChildren of a Node, calling fn each time, using this as the scole..
20227      * @param {DomNode} node node to iterate children of.
20228      * @param {Function} fn method of this class to call on each item.
20229      */
20230     iterateChildren : function(node, fn)
20231     {
20232         if (!node.childNodes.length) {
20233                 return;
20234         }
20235         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20236            fn.call(this, node.childNodes[i])
20237         }
20238     },
20239     
20240     
20241     /**
20242      * cleanTableWidths.
20243      *
20244      * Quite often pasting from word etc.. results in tables with column and widths.
20245      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20246      *
20247      */
20248     cleanTableWidths : function(node)
20249     {
20250          
20251          
20252         if (!node) {
20253             this.cleanTableWidths(this.doc.body);
20254             return;
20255         }
20256         
20257         // ignore list...
20258         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20259             return; 
20260         }
20261         Roo.log(node.tagName);
20262         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20263             this.iterateChildren(node, this.cleanTableWidths);
20264             return;
20265         }
20266         if (node.hasAttribute('width')) {
20267             node.removeAttribute('width');
20268         }
20269         
20270          
20271         if (node.hasAttribute("style")) {
20272             // pretty basic...
20273             
20274             var styles = node.getAttribute("style").split(";");
20275             var nstyle = [];
20276             Roo.each(styles, function(s) {
20277                 if (!s.match(/:/)) {
20278                     return;
20279                 }
20280                 var kv = s.split(":");
20281                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20282                     return;
20283                 }
20284                 // what ever is left... we allow.
20285                 nstyle.push(s);
20286             });
20287             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20288             if (!nstyle.length) {
20289                 node.removeAttribute('style');
20290             }
20291         }
20292         
20293         this.iterateChildren(node, this.cleanTableWidths);
20294         
20295         
20296     },
20297     
20298     
20299     
20300     
20301     domToHTML : function(currentElement, depth, nopadtext) {
20302         
20303         depth = depth || 0;
20304         nopadtext = nopadtext || false;
20305     
20306         if (!currentElement) {
20307             return this.domToHTML(this.doc.body);
20308         }
20309         
20310         //Roo.log(currentElement);
20311         var j;
20312         var allText = false;
20313         var nodeName = currentElement.nodeName;
20314         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20315         
20316         if  (nodeName == '#text') {
20317             
20318             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20319         }
20320         
20321         
20322         var ret = '';
20323         if (nodeName != 'BODY') {
20324              
20325             var i = 0;
20326             // Prints the node tagName, such as <A>, <IMG>, etc
20327             if (tagName) {
20328                 var attr = [];
20329                 for(i = 0; i < currentElement.attributes.length;i++) {
20330                     // quoting?
20331                     var aname = currentElement.attributes.item(i).name;
20332                     if (!currentElement.attributes.item(i).value.length) {
20333                         continue;
20334                     }
20335                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20336                 }
20337                 
20338                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20339             } 
20340             else {
20341                 
20342                 // eack
20343             }
20344         } else {
20345             tagName = false;
20346         }
20347         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20348             return ret;
20349         }
20350         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20351             nopadtext = true;
20352         }
20353         
20354         
20355         // Traverse the tree
20356         i = 0;
20357         var currentElementChild = currentElement.childNodes.item(i);
20358         var allText = true;
20359         var innerHTML  = '';
20360         lastnode = '';
20361         while (currentElementChild) {
20362             // Formatting code (indent the tree so it looks nice on the screen)
20363             var nopad = nopadtext;
20364             if (lastnode == 'SPAN') {
20365                 nopad  = true;
20366             }
20367             // text
20368             if  (currentElementChild.nodeName == '#text') {
20369                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20370                 toadd = nopadtext ? toadd : toadd.trim();
20371                 if (!nopad && toadd.length > 80) {
20372                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20373                 }
20374                 innerHTML  += toadd;
20375                 
20376                 i++;
20377                 currentElementChild = currentElement.childNodes.item(i);
20378                 lastNode = '';
20379                 continue;
20380             }
20381             allText = false;
20382             
20383             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20384                 
20385             // Recursively traverse the tree structure of the child node
20386             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20387             lastnode = currentElementChild.nodeName;
20388             i++;
20389             currentElementChild=currentElement.childNodes.item(i);
20390         }
20391         
20392         ret += innerHTML;
20393         
20394         if (!allText) {
20395                 // The remaining code is mostly for formatting the tree
20396             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20397         }
20398         
20399         
20400         if (tagName) {
20401             ret+= "</"+tagName+">";
20402         }
20403         return ret;
20404         
20405     },
20406         
20407     applyBlacklists : function()
20408     {
20409         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20410         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20411         
20412         this.white = [];
20413         this.black = [];
20414         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20415             if (b.indexOf(tag) > -1) {
20416                 return;
20417             }
20418             this.white.push(tag);
20419             
20420         }, this);
20421         
20422         Roo.each(w, function(tag) {
20423             if (b.indexOf(tag) > -1) {
20424                 return;
20425             }
20426             if (this.white.indexOf(tag) > -1) {
20427                 return;
20428             }
20429             this.white.push(tag);
20430             
20431         }, this);
20432         
20433         
20434         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20435             if (w.indexOf(tag) > -1) {
20436                 return;
20437             }
20438             this.black.push(tag);
20439             
20440         }, this);
20441         
20442         Roo.each(b, function(tag) {
20443             if (w.indexOf(tag) > -1) {
20444                 return;
20445             }
20446             if (this.black.indexOf(tag) > -1) {
20447                 return;
20448             }
20449             this.black.push(tag);
20450             
20451         }, this);
20452         
20453         
20454         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20455         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20456         
20457         this.cwhite = [];
20458         this.cblack = [];
20459         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20460             if (b.indexOf(tag) > -1) {
20461                 return;
20462             }
20463             this.cwhite.push(tag);
20464             
20465         }, this);
20466         
20467         Roo.each(w, function(tag) {
20468             if (b.indexOf(tag) > -1) {
20469                 return;
20470             }
20471             if (this.cwhite.indexOf(tag) > -1) {
20472                 return;
20473             }
20474             this.cwhite.push(tag);
20475             
20476         }, this);
20477         
20478         
20479         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20480             if (w.indexOf(tag) > -1) {
20481                 return;
20482             }
20483             this.cblack.push(tag);
20484             
20485         }, this);
20486         
20487         Roo.each(b, function(tag) {
20488             if (w.indexOf(tag) > -1) {
20489                 return;
20490             }
20491             if (this.cblack.indexOf(tag) > -1) {
20492                 return;
20493             }
20494             this.cblack.push(tag);
20495             
20496         }, this);
20497     },
20498     
20499     setStylesheets : function(stylesheets)
20500     {
20501         if(typeof(stylesheets) == 'string'){
20502             Roo.get(this.iframe.contentDocument.head).createChild({
20503                 tag : 'link',
20504                 rel : 'stylesheet',
20505                 type : 'text/css',
20506                 href : stylesheets
20507             });
20508             
20509             return;
20510         }
20511         var _this = this;
20512      
20513         Roo.each(stylesheets, function(s) {
20514             if(!s.length){
20515                 return;
20516             }
20517             
20518             Roo.get(_this.iframe.contentDocument.head).createChild({
20519                 tag : 'link',
20520                 rel : 'stylesheet',
20521                 type : 'text/css',
20522                 href : s
20523             });
20524         });
20525
20526         
20527     },
20528     
20529     removeStylesheets : function()
20530     {
20531         var _this = this;
20532         
20533         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20534             s.remove();
20535         });
20536     }
20537     
20538     // hide stuff that is not compatible
20539     /**
20540      * @event blur
20541      * @hide
20542      */
20543     /**
20544      * @event change
20545      * @hide
20546      */
20547     /**
20548      * @event focus
20549      * @hide
20550      */
20551     /**
20552      * @event specialkey
20553      * @hide
20554      */
20555     /**
20556      * @cfg {String} fieldClass @hide
20557      */
20558     /**
20559      * @cfg {String} focusClass @hide
20560      */
20561     /**
20562      * @cfg {String} autoCreate @hide
20563      */
20564     /**
20565      * @cfg {String} inputType @hide
20566      */
20567     /**
20568      * @cfg {String} invalidClass @hide
20569      */
20570     /**
20571      * @cfg {String} invalidText @hide
20572      */
20573     /**
20574      * @cfg {String} msgFx @hide
20575      */
20576     /**
20577      * @cfg {String} validateOnBlur @hide
20578      */
20579 });
20580
20581 Roo.HtmlEditorCore.white = [
20582         'area', 'br', 'img', 'input', 'hr', 'wbr',
20583         
20584        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20585        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20586        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20587        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20588        'table',   'ul',         'xmp', 
20589        
20590        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20591       'thead',   'tr', 
20592      
20593       'dir', 'menu', 'ol', 'ul', 'dl',
20594        
20595       'embed',  'object'
20596 ];
20597
20598
20599 Roo.HtmlEditorCore.black = [
20600     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20601         'applet', // 
20602         'base',   'basefont', 'bgsound', 'blink',  'body', 
20603         'frame',  'frameset', 'head',    'html',   'ilayer', 
20604         'iframe', 'layer',  'link',     'meta',    'object',   
20605         'script', 'style' ,'title',  'xml' // clean later..
20606 ];
20607 Roo.HtmlEditorCore.clean = [
20608     'script', 'style', 'title', 'xml'
20609 ];
20610 Roo.HtmlEditorCore.remove = [
20611     'font'
20612 ];
20613 // attributes..
20614
20615 Roo.HtmlEditorCore.ablack = [
20616     'on'
20617 ];
20618     
20619 Roo.HtmlEditorCore.aclean = [ 
20620     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20621 ];
20622
20623 // protocols..
20624 Roo.HtmlEditorCore.pwhite= [
20625         'http',  'https',  'mailto'
20626 ];
20627
20628 // white listed style attributes.
20629 Roo.HtmlEditorCore.cwhite= [
20630       //  'text-align', /// default is to allow most things..
20631       
20632          
20633 //        'font-size'//??
20634 ];
20635
20636 // black listed style attributes.
20637 Roo.HtmlEditorCore.cblack= [
20638       //  'font-size' -- this can be set by the project 
20639 ];
20640
20641
20642 Roo.HtmlEditorCore.swapCodes   =[ 
20643     [    8211, "--" ], 
20644     [    8212, "--" ], 
20645     [    8216,  "'" ],  
20646     [    8217, "'" ],  
20647     [    8220, '"' ],  
20648     [    8221, '"' ],  
20649     [    8226, "*" ],  
20650     [    8230, "..." ]
20651 ]; 
20652
20653     /*
20654  * - LGPL
20655  *
20656  * HtmlEditor
20657  * 
20658  */
20659
20660 /**
20661  * @class Roo.bootstrap.HtmlEditor
20662  * @extends Roo.bootstrap.TextArea
20663  * Bootstrap HtmlEditor class
20664
20665  * @constructor
20666  * Create a new HtmlEditor
20667  * @param {Object} config The config object
20668  */
20669
20670 Roo.bootstrap.HtmlEditor = function(config){
20671     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20672     if (!this.toolbars) {
20673         this.toolbars = [];
20674     }
20675     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20676     this.addEvents({
20677             /**
20678              * @event initialize
20679              * Fires when the editor is fully initialized (including the iframe)
20680              * @param {HtmlEditor} this
20681              */
20682             initialize: true,
20683             /**
20684              * @event activate
20685              * Fires when the editor is first receives the focus. Any insertion must wait
20686              * until after this event.
20687              * @param {HtmlEditor} this
20688              */
20689             activate: true,
20690              /**
20691              * @event beforesync
20692              * Fires before the textarea is updated with content from the editor iframe. Return false
20693              * to cancel the sync.
20694              * @param {HtmlEditor} this
20695              * @param {String} html
20696              */
20697             beforesync: true,
20698              /**
20699              * @event beforepush
20700              * Fires before the iframe editor is updated with content from the textarea. Return false
20701              * to cancel the push.
20702              * @param {HtmlEditor} this
20703              * @param {String} html
20704              */
20705             beforepush: true,
20706              /**
20707              * @event sync
20708              * Fires when the textarea is updated with content from the editor iframe.
20709              * @param {HtmlEditor} this
20710              * @param {String} html
20711              */
20712             sync: true,
20713              /**
20714              * @event push
20715              * Fires when the iframe editor is updated with content from the textarea.
20716              * @param {HtmlEditor} this
20717              * @param {String} html
20718              */
20719             push: true,
20720              /**
20721              * @event editmodechange
20722              * Fires when the editor switches edit modes
20723              * @param {HtmlEditor} this
20724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20725              */
20726             editmodechange: true,
20727             /**
20728              * @event editorevent
20729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20730              * @param {HtmlEditor} this
20731              */
20732             editorevent: true,
20733             /**
20734              * @event firstfocus
20735              * Fires when on first focus - needed by toolbars..
20736              * @param {HtmlEditor} this
20737              */
20738             firstfocus: true,
20739             /**
20740              * @event autosave
20741              * Auto save the htmlEditor value as a file into Events
20742              * @param {HtmlEditor} this
20743              */
20744             autosave: true,
20745             /**
20746              * @event savedpreview
20747              * preview the saved version of htmlEditor
20748              * @param {HtmlEditor} this
20749              */
20750             savedpreview: true
20751         });
20752 };
20753
20754
20755 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20756     
20757     
20758       /**
20759      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20760      */
20761     toolbars : false,
20762    
20763      /**
20764      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20765      *                        Roo.resizable.
20766      */
20767     resizable : false,
20768      /**
20769      * @cfg {Number} height (in pixels)
20770      */   
20771     height: 300,
20772    /**
20773      * @cfg {Number} width (in pixels)
20774      */   
20775     width: false,
20776     
20777     /**
20778      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20779      * 
20780      */
20781     stylesheets: false,
20782     
20783     // id of frame..
20784     frameId: false,
20785     
20786     // private properties
20787     validationEvent : false,
20788     deferHeight: true,
20789     initialized : false,
20790     activated : false,
20791     
20792     onFocus : Roo.emptyFn,
20793     iframePad:3,
20794     hideMode:'offsets',
20795     
20796     
20797     tbContainer : false,
20798     
20799     toolbarContainer :function() {
20800         return this.wrap.select('.x-html-editor-tb',true).first();
20801     },
20802
20803     /**
20804      * Protected method that will not generally be called directly. It
20805      * is called when the editor creates its toolbar. Override this method if you need to
20806      * add custom toolbar buttons.
20807      * @param {HtmlEditor} editor
20808      */
20809     createToolbar : function(){
20810         
20811         Roo.log("create toolbars");
20812         
20813         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20814         this.toolbars[0].render(this.toolbarContainer());
20815         
20816         return;
20817         
20818 //        if (!editor.toolbars || !editor.toolbars.length) {
20819 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20820 //        }
20821 //        
20822 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20823 //            editor.toolbars[i] = Roo.factory(
20824 //                    typeof(editor.toolbars[i]) == 'string' ?
20825 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20826 //                Roo.bootstrap.HtmlEditor);
20827 //            editor.toolbars[i].init(editor);
20828 //        }
20829     },
20830
20831      
20832     // private
20833     onRender : function(ct, position)
20834     {
20835        // Roo.log("Call onRender: " + this.xtype);
20836         var _t = this;
20837         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20838       
20839         this.wrap = this.inputEl().wrap({
20840             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20841         });
20842         
20843         this.editorcore.onRender(ct, position);
20844          
20845         if (this.resizable) {
20846             this.resizeEl = new Roo.Resizable(this.wrap, {
20847                 pinned : true,
20848                 wrap: true,
20849                 dynamic : true,
20850                 minHeight : this.height,
20851                 height: this.height,
20852                 handles : this.resizable,
20853                 width: this.width,
20854                 listeners : {
20855                     resize : function(r, w, h) {
20856                         _t.onResize(w,h); // -something
20857                     }
20858                 }
20859             });
20860             
20861         }
20862         this.createToolbar(this);
20863        
20864         
20865         if(!this.width && this.resizable){
20866             this.setSize(this.wrap.getSize());
20867         }
20868         if (this.resizeEl) {
20869             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20870             // should trigger onReize..
20871         }
20872         
20873     },
20874
20875     // private
20876     onResize : function(w, h)
20877     {
20878         Roo.log('resize: ' +w + ',' + h );
20879         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20880         var ew = false;
20881         var eh = false;
20882         
20883         if(this.inputEl() ){
20884             if(typeof w == 'number'){
20885                 var aw = w - this.wrap.getFrameWidth('lr');
20886                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20887                 ew = aw;
20888             }
20889             if(typeof h == 'number'){
20890                  var tbh = -11;  // fixme it needs to tool bar size!
20891                 for (var i =0; i < this.toolbars.length;i++) {
20892                     // fixme - ask toolbars for heights?
20893                     tbh += this.toolbars[i].el.getHeight();
20894                     //if (this.toolbars[i].footer) {
20895                     //    tbh += this.toolbars[i].footer.el.getHeight();
20896                     //}
20897                 }
20898               
20899                 
20900                 
20901                 
20902                 
20903                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20904                 ah -= 5; // knock a few pixes off for look..
20905                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20906                 var eh = ah;
20907             }
20908         }
20909         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20910         this.editorcore.onResize(ew,eh);
20911         
20912     },
20913
20914     /**
20915      * Toggles the editor between standard and source edit mode.
20916      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20917      */
20918     toggleSourceEdit : function(sourceEditMode)
20919     {
20920         this.editorcore.toggleSourceEdit(sourceEditMode);
20921         
20922         if(this.editorcore.sourceEditMode){
20923             Roo.log('editor - showing textarea');
20924             
20925 //            Roo.log('in');
20926 //            Roo.log(this.syncValue());
20927             this.syncValue();
20928             this.inputEl().removeClass(['hide', 'x-hidden']);
20929             this.inputEl().dom.removeAttribute('tabIndex');
20930             this.inputEl().focus();
20931         }else{
20932             Roo.log('editor - hiding textarea');
20933 //            Roo.log('out')
20934 //            Roo.log(this.pushValue()); 
20935             this.pushValue();
20936             
20937             this.inputEl().addClass(['hide', 'x-hidden']);
20938             this.inputEl().dom.setAttribute('tabIndex', -1);
20939             //this.deferFocus();
20940         }
20941          
20942         if(this.resizable){
20943             this.setSize(this.wrap.getSize());
20944         }
20945         
20946         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20947     },
20948  
20949     // private (for BoxComponent)
20950     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20951
20952     // private (for BoxComponent)
20953     getResizeEl : function(){
20954         return this.wrap;
20955     },
20956
20957     // private (for BoxComponent)
20958     getPositionEl : function(){
20959         return this.wrap;
20960     },
20961
20962     // private
20963     initEvents : function(){
20964         this.originalValue = this.getValue();
20965     },
20966
20967 //    /**
20968 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20969 //     * @method
20970 //     */
20971 //    markInvalid : Roo.emptyFn,
20972 //    /**
20973 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20974 //     * @method
20975 //     */
20976 //    clearInvalid : Roo.emptyFn,
20977
20978     setValue : function(v){
20979         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20980         this.editorcore.pushValue();
20981     },
20982
20983      
20984     // private
20985     deferFocus : function(){
20986         this.focus.defer(10, this);
20987     },
20988
20989     // doc'ed in Field
20990     focus : function(){
20991         this.editorcore.focus();
20992         
20993     },
20994       
20995
20996     // private
20997     onDestroy : function(){
20998         
20999         
21000         
21001         if(this.rendered){
21002             
21003             for (var i =0; i < this.toolbars.length;i++) {
21004                 // fixme - ask toolbars for heights?
21005                 this.toolbars[i].onDestroy();
21006             }
21007             
21008             this.wrap.dom.innerHTML = '';
21009             this.wrap.remove();
21010         }
21011     },
21012
21013     // private
21014     onFirstFocus : function(){
21015         //Roo.log("onFirstFocus");
21016         this.editorcore.onFirstFocus();
21017          for (var i =0; i < this.toolbars.length;i++) {
21018             this.toolbars[i].onFirstFocus();
21019         }
21020         
21021     },
21022     
21023     // private
21024     syncValue : function()
21025     {   
21026         this.editorcore.syncValue();
21027     },
21028     
21029     pushValue : function()
21030     {   
21031         this.editorcore.pushValue();
21032     }
21033      
21034     
21035     // hide stuff that is not compatible
21036     /**
21037      * @event blur
21038      * @hide
21039      */
21040     /**
21041      * @event change
21042      * @hide
21043      */
21044     /**
21045      * @event focus
21046      * @hide
21047      */
21048     /**
21049      * @event specialkey
21050      * @hide
21051      */
21052     /**
21053      * @cfg {String} fieldClass @hide
21054      */
21055     /**
21056      * @cfg {String} focusClass @hide
21057      */
21058     /**
21059      * @cfg {String} autoCreate @hide
21060      */
21061     /**
21062      * @cfg {String} inputType @hide
21063      */
21064     /**
21065      * @cfg {String} invalidClass @hide
21066      */
21067     /**
21068      * @cfg {String} invalidText @hide
21069      */
21070     /**
21071      * @cfg {String} msgFx @hide
21072      */
21073     /**
21074      * @cfg {String} validateOnBlur @hide
21075      */
21076 });
21077  
21078     
21079    
21080    
21081    
21082       
21083 Roo.namespace('Roo.bootstrap.htmleditor');
21084 /**
21085  * @class Roo.bootstrap.HtmlEditorToolbar1
21086  * Basic Toolbar
21087  * 
21088  * Usage:
21089  *
21090  new Roo.bootstrap.HtmlEditor({
21091     ....
21092     toolbars : [
21093         new Roo.bootstrap.HtmlEditorToolbar1({
21094             disable : { fonts: 1 , format: 1, ..., ... , ...],
21095             btns : [ .... ]
21096         })
21097     }
21098      
21099  * 
21100  * @cfg {Object} disable List of elements to disable..
21101  * @cfg {Array} btns List of additional buttons.
21102  * 
21103  * 
21104  * NEEDS Extra CSS? 
21105  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21106  */
21107  
21108 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21109 {
21110     
21111     Roo.apply(this, config);
21112     
21113     // default disabled, based on 'good practice'..
21114     this.disable = this.disable || {};
21115     Roo.applyIf(this.disable, {
21116         fontSize : true,
21117         colors : true,
21118         specialElements : true
21119     });
21120     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21121     
21122     this.editor = config.editor;
21123     this.editorcore = config.editor.editorcore;
21124     
21125     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21126     
21127     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21128     // dont call parent... till later.
21129 }
21130 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21131      
21132     bar : true,
21133     
21134     editor : false,
21135     editorcore : false,
21136     
21137     
21138     formats : [
21139         "p" ,  
21140         "h1","h2","h3","h4","h5","h6", 
21141         "pre", "code", 
21142         "abbr", "acronym", "address", "cite", "samp", "var",
21143         'div','span'
21144     ],
21145     
21146     onRender : function(ct, position)
21147     {
21148        // Roo.log("Call onRender: " + this.xtype);
21149         
21150        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21151        Roo.log(this.el);
21152        this.el.dom.style.marginBottom = '0';
21153        var _this = this;
21154        var editorcore = this.editorcore;
21155        var editor= this.editor;
21156        
21157        var children = [];
21158        var btn = function(id,cmd , toggle, handler){
21159        
21160             var  event = toggle ? 'toggle' : 'click';
21161        
21162             var a = {
21163                 size : 'sm',
21164                 xtype: 'Button',
21165                 xns: Roo.bootstrap,
21166                 glyphicon : id,
21167                 cmd : id || cmd,
21168                 enableToggle:toggle !== false,
21169                 //html : 'submit'
21170                 pressed : toggle ? false : null,
21171                 listeners : {}
21172             };
21173             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21174                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21175             };
21176             children.push(a);
21177             return a;
21178        }
21179         
21180         var style = {
21181                 xtype: 'Button',
21182                 size : 'sm',
21183                 xns: Roo.bootstrap,
21184                 glyphicon : 'font',
21185                 //html : 'submit'
21186                 menu : {
21187                     xtype: 'Menu',
21188                     xns: Roo.bootstrap,
21189                     items:  []
21190                 }
21191         };
21192         Roo.each(this.formats, function(f) {
21193             style.menu.items.push({
21194                 xtype :'MenuItem',
21195                 xns: Roo.bootstrap,
21196                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21197                 tagname : f,
21198                 listeners : {
21199                     click : function()
21200                     {
21201                         editorcore.insertTag(this.tagname);
21202                         editor.focus();
21203                     }
21204                 }
21205                 
21206             });
21207         });
21208          children.push(style);   
21209             
21210             
21211         btn('bold',false,true);
21212         btn('italic',false,true);
21213         btn('align-left', 'justifyleft',true);
21214         btn('align-center', 'justifycenter',true);
21215         btn('align-right' , 'justifyright',true);
21216         btn('link', false, false, function(btn) {
21217             //Roo.log("create link?");
21218             var url = prompt(this.createLinkText, this.defaultLinkValue);
21219             if(url && url != 'http:/'+'/'){
21220                 this.editorcore.relayCmd('createlink', url);
21221             }
21222         }),
21223         btn('list','insertunorderedlist',true);
21224         btn('pencil', false,true, function(btn){
21225                 Roo.log(this);
21226                 
21227                 this.toggleSourceEdit(btn.pressed);
21228         });
21229         /*
21230         var cog = {
21231                 xtype: 'Button',
21232                 size : 'sm',
21233                 xns: Roo.bootstrap,
21234                 glyphicon : 'cog',
21235                 //html : 'submit'
21236                 menu : {
21237                     xtype: 'Menu',
21238                     xns: Roo.bootstrap,
21239                     items:  []
21240                 }
21241         };
21242         
21243         cog.menu.items.push({
21244             xtype :'MenuItem',
21245             xns: Roo.bootstrap,
21246             html : Clean styles,
21247             tagname : f,
21248             listeners : {
21249                 click : function()
21250                 {
21251                     editorcore.insertTag(this.tagname);
21252                     editor.focus();
21253                 }
21254             }
21255             
21256         });
21257        */
21258         
21259          
21260        this.xtype = 'NavSimplebar';
21261         
21262         for(var i=0;i< children.length;i++) {
21263             
21264             this.buttons.add(this.addxtypeChild(children[i]));
21265             
21266         }
21267         
21268         editor.on('editorevent', this.updateToolbar, this);
21269     },
21270     onBtnClick : function(id)
21271     {
21272        this.editorcore.relayCmd(id);
21273        this.editorcore.focus();
21274     },
21275     
21276     /**
21277      * Protected method that will not generally be called directly. It triggers
21278      * a toolbar update by reading the markup state of the current selection in the editor.
21279      */
21280     updateToolbar: function(){
21281
21282         if(!this.editorcore.activated){
21283             this.editor.onFirstFocus(); // is this neeed?
21284             return;
21285         }
21286
21287         var btns = this.buttons; 
21288         var doc = this.editorcore.doc;
21289         btns.get('bold').setActive(doc.queryCommandState('bold'));
21290         btns.get('italic').setActive(doc.queryCommandState('italic'));
21291         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21292         
21293         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21294         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21295         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21296         
21297         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21298         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21299          /*
21300         
21301         var ans = this.editorcore.getAllAncestors();
21302         if (this.formatCombo) {
21303             
21304             
21305             var store = this.formatCombo.store;
21306             this.formatCombo.setValue("");
21307             for (var i =0; i < ans.length;i++) {
21308                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21309                     // select it..
21310                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21311                     break;
21312                 }
21313             }
21314         }
21315         
21316         
21317         
21318         // hides menus... - so this cant be on a menu...
21319         Roo.bootstrap.MenuMgr.hideAll();
21320         */
21321         Roo.bootstrap.MenuMgr.hideAll();
21322         //this.editorsyncValue();
21323     },
21324     onFirstFocus: function() {
21325         this.buttons.each(function(item){
21326            item.enable();
21327         });
21328     },
21329     toggleSourceEdit : function(sourceEditMode){
21330         
21331           
21332         if(sourceEditMode){
21333             Roo.log("disabling buttons");
21334            this.buttons.each( function(item){
21335                 if(item.cmd != 'pencil'){
21336                     item.disable();
21337                 }
21338             });
21339           
21340         }else{
21341             Roo.log("enabling buttons");
21342             if(this.editorcore.initialized){
21343                 this.buttons.each( function(item){
21344                     item.enable();
21345                 });
21346             }
21347             
21348         }
21349         Roo.log("calling toggole on editor");
21350         // tell the editor that it's been pressed..
21351         this.editor.toggleSourceEdit(sourceEditMode);
21352        
21353     }
21354 });
21355
21356
21357
21358
21359
21360 /**
21361  * @class Roo.bootstrap.Table.AbstractSelectionModel
21362  * @extends Roo.util.Observable
21363  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21364  * implemented by descendant classes.  This class should not be directly instantiated.
21365  * @constructor
21366  */
21367 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21368     this.locked = false;
21369     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21370 };
21371
21372
21373 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21374     /** @ignore Called by the grid automatically. Do not call directly. */
21375     init : function(grid){
21376         this.grid = grid;
21377         this.initEvents();
21378     },
21379
21380     /**
21381      * Locks the selections.
21382      */
21383     lock : function(){
21384         this.locked = true;
21385     },
21386
21387     /**
21388      * Unlocks the selections.
21389      */
21390     unlock : function(){
21391         this.locked = false;
21392     },
21393
21394     /**
21395      * Returns true if the selections are locked.
21396      * @return {Boolean}
21397      */
21398     isLocked : function(){
21399         return this.locked;
21400     }
21401 });
21402 /**
21403  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21404  * @class Roo.bootstrap.Table.RowSelectionModel
21405  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21406  * It supports multiple selections and keyboard selection/navigation. 
21407  * @constructor
21408  * @param {Object} config
21409  */
21410
21411 Roo.bootstrap.Table.RowSelectionModel = function(config){
21412     Roo.apply(this, config);
21413     this.selections = new Roo.util.MixedCollection(false, function(o){
21414         return o.id;
21415     });
21416
21417     this.last = false;
21418     this.lastActive = false;
21419
21420     this.addEvents({
21421         /**
21422              * @event selectionchange
21423              * Fires when the selection changes
21424              * @param {SelectionModel} this
21425              */
21426             "selectionchange" : true,
21427         /**
21428              * @event afterselectionchange
21429              * Fires after the selection changes (eg. by key press or clicking)
21430              * @param {SelectionModel} this
21431              */
21432             "afterselectionchange" : true,
21433         /**
21434              * @event beforerowselect
21435              * Fires when a row is selected being selected, return false to cancel.
21436              * @param {SelectionModel} this
21437              * @param {Number} rowIndex The selected index
21438              * @param {Boolean} keepExisting False if other selections will be cleared
21439              */
21440             "beforerowselect" : true,
21441         /**
21442              * @event rowselect
21443              * Fires when a row is selected.
21444              * @param {SelectionModel} this
21445              * @param {Number} rowIndex The selected index
21446              * @param {Roo.data.Record} r The record
21447              */
21448             "rowselect" : true,
21449         /**
21450              * @event rowdeselect
21451              * Fires when a row is deselected.
21452              * @param {SelectionModel} this
21453              * @param {Number} rowIndex The selected index
21454              */
21455         "rowdeselect" : true
21456     });
21457     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21458     this.locked = false;
21459 };
21460
21461 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21462     /**
21463      * @cfg {Boolean} singleSelect
21464      * True to allow selection of only one row at a time (defaults to false)
21465      */
21466     singleSelect : false,
21467
21468     // private
21469     initEvents : function(){
21470
21471         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21472             this.grid.on("mousedown", this.handleMouseDown, this);
21473         }else{ // allow click to work like normal
21474             this.grid.on("rowclick", this.handleDragableRowClick, this);
21475         }
21476
21477         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21478             "up" : function(e){
21479                 if(!e.shiftKey){
21480                     this.selectPrevious(e.shiftKey);
21481                 }else if(this.last !== false && this.lastActive !== false){
21482                     var last = this.last;
21483                     this.selectRange(this.last,  this.lastActive-1);
21484                     this.grid.getView().focusRow(this.lastActive);
21485                     if(last !== false){
21486                         this.last = last;
21487                     }
21488                 }else{
21489                     this.selectFirstRow();
21490                 }
21491                 this.fireEvent("afterselectionchange", this);
21492             },
21493             "down" : function(e){
21494                 if(!e.shiftKey){
21495                     this.selectNext(e.shiftKey);
21496                 }else if(this.last !== false && this.lastActive !== false){
21497                     var last = this.last;
21498                     this.selectRange(this.last,  this.lastActive+1);
21499                     this.grid.getView().focusRow(this.lastActive);
21500                     if(last !== false){
21501                         this.last = last;
21502                     }
21503                 }else{
21504                     this.selectFirstRow();
21505                 }
21506                 this.fireEvent("afterselectionchange", this);
21507             },
21508             scope: this
21509         });
21510
21511         var view = this.grid.view;
21512         view.on("refresh", this.onRefresh, this);
21513         view.on("rowupdated", this.onRowUpdated, this);
21514         view.on("rowremoved", this.onRemove, this);
21515     },
21516
21517     // private
21518     onRefresh : function(){
21519         var ds = this.grid.dataSource, i, v = this.grid.view;
21520         var s = this.selections;
21521         s.each(function(r){
21522             if((i = ds.indexOfId(r.id)) != -1){
21523                 v.onRowSelect(i);
21524             }else{
21525                 s.remove(r);
21526             }
21527         });
21528     },
21529
21530     // private
21531     onRemove : function(v, index, r){
21532         this.selections.remove(r);
21533     },
21534
21535     // private
21536     onRowUpdated : function(v, index, r){
21537         if(this.isSelected(r)){
21538             v.onRowSelect(index);
21539         }
21540     },
21541
21542     /**
21543      * Select records.
21544      * @param {Array} records The records to select
21545      * @param {Boolean} keepExisting (optional) True to keep existing selections
21546      */
21547     selectRecords : function(records, keepExisting){
21548         if(!keepExisting){
21549             this.clearSelections();
21550         }
21551         var ds = this.grid.dataSource;
21552         for(var i = 0, len = records.length; i < len; i++){
21553             this.selectRow(ds.indexOf(records[i]), true);
21554         }
21555     },
21556
21557     /**
21558      * Gets the number of selected rows.
21559      * @return {Number}
21560      */
21561     getCount : function(){
21562         return this.selections.length;
21563     },
21564
21565     /**
21566      * Selects the first row in the grid.
21567      */
21568     selectFirstRow : function(){
21569         this.selectRow(0);
21570     },
21571
21572     /**
21573      * Select the last row.
21574      * @param {Boolean} keepExisting (optional) True to keep existing selections
21575      */
21576     selectLastRow : function(keepExisting){
21577         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21578     },
21579
21580     /**
21581      * Selects the row immediately following the last selected row.
21582      * @param {Boolean} keepExisting (optional) True to keep existing selections
21583      */
21584     selectNext : function(keepExisting){
21585         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21586             this.selectRow(this.last+1, keepExisting);
21587             this.grid.getView().focusRow(this.last);
21588         }
21589     },
21590
21591     /**
21592      * Selects the row that precedes the last selected row.
21593      * @param {Boolean} keepExisting (optional) True to keep existing selections
21594      */
21595     selectPrevious : function(keepExisting){
21596         if(this.last){
21597             this.selectRow(this.last-1, keepExisting);
21598             this.grid.getView().focusRow(this.last);
21599         }
21600     },
21601
21602     /**
21603      * Returns the selected records
21604      * @return {Array} Array of selected records
21605      */
21606     getSelections : function(){
21607         return [].concat(this.selections.items);
21608     },
21609
21610     /**
21611      * Returns the first selected record.
21612      * @return {Record}
21613      */
21614     getSelected : function(){
21615         return this.selections.itemAt(0);
21616     },
21617
21618
21619     /**
21620      * Clears all selections.
21621      */
21622     clearSelections : function(fast){
21623         if(this.locked) return;
21624         if(fast !== true){
21625             var ds = this.grid.dataSource;
21626             var s = this.selections;
21627             s.each(function(r){
21628                 this.deselectRow(ds.indexOfId(r.id));
21629             }, this);
21630             s.clear();
21631         }else{
21632             this.selections.clear();
21633         }
21634         this.last = false;
21635     },
21636
21637
21638     /**
21639      * Selects all rows.
21640      */
21641     selectAll : function(){
21642         if(this.locked) return;
21643         this.selections.clear();
21644         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21645             this.selectRow(i, true);
21646         }
21647     },
21648
21649     /**
21650      * Returns True if there is a selection.
21651      * @return {Boolean}
21652      */
21653     hasSelection : function(){
21654         return this.selections.length > 0;
21655     },
21656
21657     /**
21658      * Returns True if the specified row is selected.
21659      * @param {Number/Record} record The record or index of the record to check
21660      * @return {Boolean}
21661      */
21662     isSelected : function(index){
21663         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21664         return (r && this.selections.key(r.id) ? true : false);
21665     },
21666
21667     /**
21668      * Returns True if the specified record id is selected.
21669      * @param {String} id The id of record to check
21670      * @return {Boolean}
21671      */
21672     isIdSelected : function(id){
21673         return (this.selections.key(id) ? true : false);
21674     },
21675
21676     // private
21677     handleMouseDown : function(e, t){
21678         var view = this.grid.getView(), rowIndex;
21679         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21680             return;
21681         };
21682         if(e.shiftKey && this.last !== false){
21683             var last = this.last;
21684             this.selectRange(last, rowIndex, e.ctrlKey);
21685             this.last = last; // reset the last
21686             view.focusRow(rowIndex);
21687         }else{
21688             var isSelected = this.isSelected(rowIndex);
21689             if(e.button !== 0 && isSelected){
21690                 view.focusRow(rowIndex);
21691             }else if(e.ctrlKey && isSelected){
21692                 this.deselectRow(rowIndex);
21693             }else if(!isSelected){
21694                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21695                 view.focusRow(rowIndex);
21696             }
21697         }
21698         this.fireEvent("afterselectionchange", this);
21699     },
21700     // private
21701     handleDragableRowClick :  function(grid, rowIndex, e) 
21702     {
21703         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21704             this.selectRow(rowIndex, false);
21705             grid.view.focusRow(rowIndex);
21706              this.fireEvent("afterselectionchange", this);
21707         }
21708     },
21709     
21710     /**
21711      * Selects multiple rows.
21712      * @param {Array} rows Array of the indexes of the row to select
21713      * @param {Boolean} keepExisting (optional) True to keep existing selections
21714      */
21715     selectRows : function(rows, keepExisting){
21716         if(!keepExisting){
21717             this.clearSelections();
21718         }
21719         for(var i = 0, len = rows.length; i < len; i++){
21720             this.selectRow(rows[i], true);
21721         }
21722     },
21723
21724     /**
21725      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21726      * @param {Number} startRow The index of the first row in the range
21727      * @param {Number} endRow The index of the last row in the range
21728      * @param {Boolean} keepExisting (optional) True to retain existing selections
21729      */
21730     selectRange : function(startRow, endRow, keepExisting){
21731         if(this.locked) return;
21732         if(!keepExisting){
21733             this.clearSelections();
21734         }
21735         if(startRow <= endRow){
21736             for(var i = startRow; i <= endRow; i++){
21737                 this.selectRow(i, true);
21738             }
21739         }else{
21740             for(var i = startRow; i >= endRow; i--){
21741                 this.selectRow(i, true);
21742             }
21743         }
21744     },
21745
21746     /**
21747      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21748      * @param {Number} startRow The index of the first row in the range
21749      * @param {Number} endRow The index of the last row in the range
21750      */
21751     deselectRange : function(startRow, endRow, preventViewNotify){
21752         if(this.locked) return;
21753         for(var i = startRow; i <= endRow; i++){
21754             this.deselectRow(i, preventViewNotify);
21755         }
21756     },
21757
21758     /**
21759      * Selects a row.
21760      * @param {Number} row The index of the row to select
21761      * @param {Boolean} keepExisting (optional) True to keep existing selections
21762      */
21763     selectRow : function(index, keepExisting, preventViewNotify){
21764         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21765         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21766             if(!keepExisting || this.singleSelect){
21767                 this.clearSelections();
21768             }
21769             var r = this.grid.dataSource.getAt(index);
21770             this.selections.add(r);
21771             this.last = this.lastActive = index;
21772             if(!preventViewNotify){
21773                 this.grid.getView().onRowSelect(index);
21774             }
21775             this.fireEvent("rowselect", this, index, r);
21776             this.fireEvent("selectionchange", this);
21777         }
21778     },
21779
21780     /**
21781      * Deselects a row.
21782      * @param {Number} row The index of the row to deselect
21783      */
21784     deselectRow : function(index, preventViewNotify){
21785         if(this.locked) return;
21786         if(this.last == index){
21787             this.last = false;
21788         }
21789         if(this.lastActive == index){
21790             this.lastActive = false;
21791         }
21792         var r = this.grid.dataSource.getAt(index);
21793         this.selections.remove(r);
21794         if(!preventViewNotify){
21795             this.grid.getView().onRowDeselect(index);
21796         }
21797         this.fireEvent("rowdeselect", this, index);
21798         this.fireEvent("selectionchange", this);
21799     },
21800
21801     // private
21802     restoreLast : function(){
21803         if(this._last){
21804             this.last = this._last;
21805         }
21806     },
21807
21808     // private
21809     acceptsNav : function(row, col, cm){
21810         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21811     },
21812
21813     // private
21814     onEditorKey : function(field, e){
21815         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21816         if(k == e.TAB){
21817             e.stopEvent();
21818             ed.completeEdit();
21819             if(e.shiftKey){
21820                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21821             }else{
21822                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21823             }
21824         }else if(k == e.ENTER && !e.ctrlKey){
21825             e.stopEvent();
21826             ed.completeEdit();
21827             if(e.shiftKey){
21828                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21829             }else{
21830                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21831             }
21832         }else if(k == e.ESC){
21833             ed.cancelEdit();
21834         }
21835         if(newCell){
21836             g.startEditing(newCell[0], newCell[1]);
21837         }
21838     }
21839 });/*
21840  * Based on:
21841  * Ext JS Library 1.1.1
21842  * Copyright(c) 2006-2007, Ext JS, LLC.
21843  *
21844  * Originally Released Under LGPL - original licence link has changed is not relivant.
21845  *
21846  * Fork - LGPL
21847  * <script type="text/javascript">
21848  */
21849  
21850 /**
21851  * @class Roo.bootstrap.PagingToolbar
21852  * @extends Roo.bootstrap.NavSimplebar
21853  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21854  * @constructor
21855  * Create a new PagingToolbar
21856  * @param {Object} config The config object
21857  * @param {Roo.data.Store} store
21858  */
21859 Roo.bootstrap.PagingToolbar = function(config)
21860 {
21861     // old args format still supported... - xtype is prefered..
21862         // created from xtype...
21863     
21864     this.ds = config.dataSource;
21865     
21866     if (config.store && !this.ds) {
21867         this.store= Roo.factory(config.store, Roo.data);
21868         this.ds = this.store;
21869         this.ds.xmodule = this.xmodule || false;
21870     }
21871     
21872     this.toolbarItems = [];
21873     if (config.items) {
21874         this.toolbarItems = config.items;
21875     }
21876     
21877     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21878     
21879     this.cursor = 0;
21880     
21881     if (this.ds) { 
21882         this.bind(this.ds);
21883     }
21884     
21885     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21886     
21887 };
21888
21889 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21890     /**
21891      * @cfg {Roo.data.Store} dataSource
21892      * The underlying data store providing the paged data
21893      */
21894     /**
21895      * @cfg {String/HTMLElement/Element} container
21896      * container The id or element that will contain the toolbar
21897      */
21898     /**
21899      * @cfg {Boolean} displayInfo
21900      * True to display the displayMsg (defaults to false)
21901      */
21902     /**
21903      * @cfg {Number} pageSize
21904      * The number of records to display per page (defaults to 20)
21905      */
21906     pageSize: 20,
21907     /**
21908      * @cfg {String} displayMsg
21909      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21910      */
21911     displayMsg : 'Displaying {0} - {1} of {2}',
21912     /**
21913      * @cfg {String} emptyMsg
21914      * The message to display when no records are found (defaults to "No data to display")
21915      */
21916     emptyMsg : 'No data to display',
21917     /**
21918      * Customizable piece of the default paging text (defaults to "Page")
21919      * @type String
21920      */
21921     beforePageText : "Page",
21922     /**
21923      * Customizable piece of the default paging text (defaults to "of %0")
21924      * @type String
21925      */
21926     afterPageText : "of {0}",
21927     /**
21928      * Customizable piece of the default paging text (defaults to "First Page")
21929      * @type String
21930      */
21931     firstText : "First Page",
21932     /**
21933      * Customizable piece of the default paging text (defaults to "Previous Page")
21934      * @type String
21935      */
21936     prevText : "Previous Page",
21937     /**
21938      * Customizable piece of the default paging text (defaults to "Next Page")
21939      * @type String
21940      */
21941     nextText : "Next Page",
21942     /**
21943      * Customizable piece of the default paging text (defaults to "Last Page")
21944      * @type String
21945      */
21946     lastText : "Last Page",
21947     /**
21948      * Customizable piece of the default paging text (defaults to "Refresh")
21949      * @type String
21950      */
21951     refreshText : "Refresh",
21952
21953     buttons : false,
21954     // private
21955     onRender : function(ct, position) 
21956     {
21957         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21958         this.navgroup.parentId = this.id;
21959         this.navgroup.onRender(this.el, null);
21960         // add the buttons to the navgroup
21961         
21962         if(this.displayInfo){
21963             Roo.log(this.el.select('ul.navbar-nav',true).first());
21964             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21965             this.displayEl = this.el.select('.x-paging-info', true).first();
21966 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21967 //            this.displayEl = navel.el.select('span',true).first();
21968         }
21969         
21970         var _this = this;
21971         
21972         if(this.buttons){
21973             Roo.each(_this.buttons, function(e){ // this might need to use render????
21974                Roo.factory(e).onRender(_this.el, null);
21975             });
21976         }
21977             
21978         Roo.each(_this.toolbarItems, function(e) {
21979             _this.navgroup.addItem(e);
21980         });
21981         
21982         
21983         this.first = this.navgroup.addItem({
21984             tooltip: this.firstText,
21985             cls: "prev",
21986             icon : 'fa fa-backward',
21987             disabled: true,
21988             preventDefault: true,
21989             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21990         });
21991         
21992         this.prev =  this.navgroup.addItem({
21993             tooltip: this.prevText,
21994             cls: "prev",
21995             icon : 'fa fa-step-backward',
21996             disabled: true,
21997             preventDefault: true,
21998             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21999         });
22000     //this.addSeparator();
22001         
22002         
22003         var field = this.navgroup.addItem( {
22004             tagtype : 'span',
22005             cls : 'x-paging-position',
22006             
22007             html : this.beforePageText  +
22008                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22009                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22010          } ); //?? escaped?
22011         
22012         this.field = field.el.select('input', true).first();
22013         this.field.on("keydown", this.onPagingKeydown, this);
22014         this.field.on("focus", function(){this.dom.select();});
22015     
22016     
22017         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22018         //this.field.setHeight(18);
22019         //this.addSeparator();
22020         this.next = this.navgroup.addItem({
22021             tooltip: this.nextText,
22022             cls: "next",
22023             html : ' <i class="fa fa-step-forward">',
22024             disabled: true,
22025             preventDefault: true,
22026             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22027         });
22028         this.last = this.navgroup.addItem({
22029             tooltip: this.lastText,
22030             icon : 'fa fa-forward',
22031             cls: "next",
22032             disabled: true,
22033             preventDefault: true,
22034             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22035         });
22036     //this.addSeparator();
22037         this.loading = this.navgroup.addItem({
22038             tooltip: this.refreshText,
22039             icon: 'fa fa-refresh',
22040             preventDefault: true,
22041             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22042         });
22043         
22044     },
22045
22046     // private
22047     updateInfo : function(){
22048         if(this.displayEl){
22049             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22050             var msg = count == 0 ?
22051                 this.emptyMsg :
22052                 String.format(
22053                     this.displayMsg,
22054                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22055                 );
22056             this.displayEl.update(msg);
22057         }
22058     },
22059
22060     // private
22061     onLoad : function(ds, r, o){
22062        this.cursor = o.params ? o.params.start : 0;
22063        var d = this.getPageData(),
22064             ap = d.activePage,
22065             ps = d.pages;
22066         
22067        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22068        this.field.dom.value = ap;
22069        this.first.setDisabled(ap == 1);
22070        this.prev.setDisabled(ap == 1);
22071        this.next.setDisabled(ap == ps);
22072        this.last.setDisabled(ap == ps);
22073        this.loading.enable();
22074        this.updateInfo();
22075     },
22076
22077     // private
22078     getPageData : function(){
22079         var total = this.ds.getTotalCount();
22080         return {
22081             total : total,
22082             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22083             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22084         };
22085     },
22086
22087     // private
22088     onLoadError : function(){
22089         this.loading.enable();
22090     },
22091
22092     // private
22093     onPagingKeydown : function(e){
22094         var k = e.getKey();
22095         var d = this.getPageData();
22096         if(k == e.RETURN){
22097             var v = this.field.dom.value, pageNum;
22098             if(!v || isNaN(pageNum = parseInt(v, 10))){
22099                 this.field.dom.value = d.activePage;
22100                 return;
22101             }
22102             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22103             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22104             e.stopEvent();
22105         }
22106         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))
22107         {
22108           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22109           this.field.dom.value = pageNum;
22110           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22111           e.stopEvent();
22112         }
22113         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22114         {
22115           var v = this.field.dom.value, pageNum; 
22116           var increment = (e.shiftKey) ? 10 : 1;
22117           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22118             increment *= -1;
22119           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22120             this.field.dom.value = d.activePage;
22121             return;
22122           }
22123           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22124           {
22125             this.field.dom.value = parseInt(v, 10) + increment;
22126             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22127             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22128           }
22129           e.stopEvent();
22130         }
22131     },
22132
22133     // private
22134     beforeLoad : function(){
22135         if(this.loading){
22136             this.loading.disable();
22137         }
22138     },
22139
22140     // private
22141     onClick : function(which){
22142         
22143         var ds = this.ds;
22144         if (!ds) {
22145             return;
22146         }
22147         
22148         switch(which){
22149             case "first":
22150                 ds.load({params:{start: 0, limit: this.pageSize}});
22151             break;
22152             case "prev":
22153                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22154             break;
22155             case "next":
22156                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22157             break;
22158             case "last":
22159                 var total = ds.getTotalCount();
22160                 var extra = total % this.pageSize;
22161                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22162                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22163             break;
22164             case "refresh":
22165                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22166             break;
22167         }
22168     },
22169
22170     /**
22171      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22172      * @param {Roo.data.Store} store The data store to unbind
22173      */
22174     unbind : function(ds){
22175         ds.un("beforeload", this.beforeLoad, this);
22176         ds.un("load", this.onLoad, this);
22177         ds.un("loadexception", this.onLoadError, this);
22178         ds.un("remove", this.updateInfo, this);
22179         ds.un("add", this.updateInfo, this);
22180         this.ds = undefined;
22181     },
22182
22183     /**
22184      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22185      * @param {Roo.data.Store} store The data store to bind
22186      */
22187     bind : function(ds){
22188         ds.on("beforeload", this.beforeLoad, this);
22189         ds.on("load", this.onLoad, this);
22190         ds.on("loadexception", this.onLoadError, this);
22191         ds.on("remove", this.updateInfo, this);
22192         ds.on("add", this.updateInfo, this);
22193         this.ds = ds;
22194     }
22195 });/*
22196  * - LGPL
22197  *
22198  * element
22199  * 
22200  */
22201
22202 /**
22203  * @class Roo.bootstrap.MessageBar
22204  * @extends Roo.bootstrap.Component
22205  * Bootstrap MessageBar class
22206  * @cfg {String} html contents of the MessageBar
22207  * @cfg {String} weight (info | success | warning | danger) default info
22208  * @cfg {String} beforeClass insert the bar before the given class
22209  * @cfg {Boolean} closable (true | false) default false
22210  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22211  * 
22212  * @constructor
22213  * Create a new Element
22214  * @param {Object} config The config object
22215  */
22216
22217 Roo.bootstrap.MessageBar = function(config){
22218     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22219 };
22220
22221 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22222     
22223     html: '',
22224     weight: 'info',
22225     closable: false,
22226     fixed: false,
22227     beforeClass: 'bootstrap-sticky-wrap',
22228     
22229     getAutoCreate : function(){
22230         
22231         var cfg = {
22232             tag: 'div',
22233             cls: 'alert alert-dismissable alert-' + this.weight,
22234             cn: [
22235                 {
22236                     tag: 'span',
22237                     cls: 'message',
22238                     html: this.html || ''
22239                 }
22240             ]
22241         };
22242         
22243         if(this.fixed){
22244             cfg.cls += ' alert-messages-fixed';
22245         }
22246         
22247         if(this.closable){
22248             cfg.cn.push({
22249                 tag: 'button',
22250                 cls: 'close',
22251                 html: 'x'
22252             });
22253         }
22254         
22255         return cfg;
22256     },
22257     
22258     onRender : function(ct, position)
22259     {
22260         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22261         
22262         if(!this.el){
22263             var cfg = Roo.apply({},  this.getAutoCreate());
22264             cfg.id = Roo.id();
22265             
22266             if (this.cls) {
22267                 cfg.cls += ' ' + this.cls;
22268             }
22269             if (this.style) {
22270                 cfg.style = this.style;
22271             }
22272             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22273             
22274             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22275         }
22276         
22277         this.el.select('>button.close').on('click', this.hide, this);
22278         
22279     },
22280     
22281     show : function()
22282     {
22283         if (!this.rendered) {
22284             this.render();
22285         }
22286         
22287         this.el.show();
22288         
22289         this.fireEvent('show', this);
22290         
22291     },
22292     
22293     hide : function()
22294     {
22295         if (!this.rendered) {
22296             this.render();
22297         }
22298         
22299         this.el.hide();
22300         
22301         this.fireEvent('hide', this);
22302     },
22303     
22304     update : function()
22305     {
22306 //        var e = this.el.dom.firstChild;
22307 //        
22308 //        if(this.closable){
22309 //            e = e.nextSibling;
22310 //        }
22311 //        
22312 //        e.data = this.html || '';
22313
22314         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22315     }
22316    
22317 });
22318
22319  
22320
22321      /*
22322  * - LGPL
22323  *
22324  * Graph
22325  * 
22326  */
22327
22328
22329 /**
22330  * @class Roo.bootstrap.Graph
22331  * @extends Roo.bootstrap.Component
22332  * Bootstrap Graph class
22333 > Prameters
22334  -sm {number} sm 4
22335  -md {number} md 5
22336  @cfg {String} graphtype  bar | vbar | pie
22337  @cfg {number} g_x coodinator | centre x (pie)
22338  @cfg {number} g_y coodinator | centre y (pie)
22339  @cfg {number} g_r radius (pie)
22340  @cfg {number} g_height height of the chart (respected by all elements in the set)
22341  @cfg {number} g_width width of the chart (respected by all elements in the set)
22342  @cfg {Object} title The title of the chart
22343     
22344  -{Array}  values
22345  -opts (object) options for the chart 
22346      o {
22347      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22348      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22349      o vgutter (number)
22350      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.
22351      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22352      o to
22353      o stretch (boolean)
22354      o }
22355  -opts (object) options for the pie
22356      o{
22357      o cut
22358      o startAngle (number)
22359      o endAngle (number)
22360      } 
22361  *
22362  * @constructor
22363  * Create a new Input
22364  * @param {Object} config The config object
22365  */
22366
22367 Roo.bootstrap.Graph = function(config){
22368     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22369     
22370     this.addEvents({
22371         // img events
22372         /**
22373          * @event click
22374          * The img click event for the img.
22375          * @param {Roo.EventObject} e
22376          */
22377         "click" : true
22378     });
22379 };
22380
22381 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22382     
22383     sm: 4,
22384     md: 5,
22385     graphtype: 'bar',
22386     g_height: 250,
22387     g_width: 400,
22388     g_x: 50,
22389     g_y: 50,
22390     g_r: 30,
22391     opts:{
22392         //g_colors: this.colors,
22393         g_type: 'soft',
22394         g_gutter: '20%'
22395
22396     },
22397     title : false,
22398
22399     getAutoCreate : function(){
22400         
22401         var cfg = {
22402             tag: 'div',
22403             html : null
22404         };
22405         
22406         
22407         return  cfg;
22408     },
22409
22410     onRender : function(ct,position){
22411         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22412         this.raphael = Raphael(this.el.dom);
22413         
22414                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22415                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22416                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22417                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22418                 /*
22419                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22420                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22421                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22422                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22423                 
22424                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22425                 r.barchart(330, 10, 300, 220, data1);
22426                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22427                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22428                 */
22429                 
22430                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22431                 // r.barchart(30, 30, 560, 250,  xdata, {
22432                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22433                 //     axis : "0 0 1 1",
22434                 //     axisxlabels :  xdata
22435                 //     //yvalues : cols,
22436                    
22437                 // });
22438 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22439 //        
22440 //        this.load(null,xdata,{
22441 //                axis : "0 0 1 1",
22442 //                axisxlabels :  xdata
22443 //                });
22444
22445     },
22446
22447     load : function(graphtype,xdata,opts){
22448         this.raphael.clear();
22449         if(!graphtype) {
22450             graphtype = this.graphtype;
22451         }
22452         if(!opts){
22453             opts = this.opts;
22454         }
22455         var r = this.raphael,
22456             fin = function () {
22457                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22458             },
22459             fout = function () {
22460                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22461             },
22462             pfin = function() {
22463                 this.sector.stop();
22464                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22465
22466                 if (this.label) {
22467                     this.label[0].stop();
22468                     this.label[0].attr({ r: 7.5 });
22469                     this.label[1].attr({ "font-weight": 800 });
22470                 }
22471             },
22472             pfout = function() {
22473                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22474
22475                 if (this.label) {
22476                     this.label[0].animate({ r: 5 }, 500, "bounce");
22477                     this.label[1].attr({ "font-weight": 400 });
22478                 }
22479             };
22480
22481         switch(graphtype){
22482             case 'bar':
22483                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22484                 break;
22485             case 'hbar':
22486                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22487                 break;
22488             case 'pie':
22489 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22490 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22491 //            
22492                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22493                 
22494                 break;
22495
22496         }
22497         
22498         if(this.title){
22499             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22500         }
22501         
22502     },
22503     
22504     setTitle: function(o)
22505     {
22506         this.title = o;
22507     },
22508     
22509     initEvents: function() {
22510         
22511         if(!this.href){
22512             this.el.on('click', this.onClick, this);
22513         }
22514     },
22515     
22516     onClick : function(e)
22517     {
22518         Roo.log('img onclick');
22519         this.fireEvent('click', this, e);
22520     }
22521    
22522 });
22523
22524  
22525 /*
22526  * - LGPL
22527  *
22528  * numberBox
22529  * 
22530  */
22531 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22532
22533 /**
22534  * @class Roo.bootstrap.dash.NumberBox
22535  * @extends Roo.bootstrap.Component
22536  * Bootstrap NumberBox class
22537  * @cfg {String} headline Box headline
22538  * @cfg {String} content Box content
22539  * @cfg {String} icon Box icon
22540  * @cfg {String} footer Footer text
22541  * @cfg {String} fhref Footer href
22542  * 
22543  * @constructor
22544  * Create a new NumberBox
22545  * @param {Object} config The config object
22546  */
22547
22548
22549 Roo.bootstrap.dash.NumberBox = function(config){
22550     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22551     
22552 };
22553
22554 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22555     
22556     headline : '',
22557     content : '',
22558     icon : '',
22559     footer : '',
22560     fhref : '',
22561     ficon : '',
22562     
22563     getAutoCreate : function(){
22564         
22565         var cfg = {
22566             tag : 'div',
22567             cls : 'small-box ',
22568             cn : [
22569                 {
22570                     tag : 'div',
22571                     cls : 'inner',
22572                     cn :[
22573                         {
22574                             tag : 'h3',
22575                             cls : 'roo-headline',
22576                             html : this.headline
22577                         },
22578                         {
22579                             tag : 'p',
22580                             cls : 'roo-content',
22581                             html : this.content
22582                         }
22583                     ]
22584                 }
22585             ]
22586         };
22587         
22588         if(this.icon){
22589             cfg.cn.push({
22590                 tag : 'div',
22591                 cls : 'icon',
22592                 cn :[
22593                     {
22594                         tag : 'i',
22595                         cls : 'ion ' + this.icon
22596                     }
22597                 ]
22598             });
22599         }
22600         
22601         if(this.footer){
22602             var footer = {
22603                 tag : 'a',
22604                 cls : 'small-box-footer',
22605                 href : this.fhref || '#',
22606                 html : this.footer
22607             };
22608             
22609             cfg.cn.push(footer);
22610             
22611         }
22612         
22613         return  cfg;
22614     },
22615
22616     onRender : function(ct,position){
22617         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22618
22619
22620        
22621                 
22622     },
22623
22624     setHeadline: function (value)
22625     {
22626         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22627     },
22628     
22629     setFooter: function (value, href)
22630     {
22631         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22632         
22633         if(href){
22634             this.el.select('a.small-box-footer',true).first().attr('href', href);
22635         }
22636         
22637     },
22638
22639     setContent: function (value)
22640     {
22641         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22642     },
22643
22644     initEvents: function() 
22645     {   
22646         
22647     }
22648     
22649 });
22650
22651  
22652 /*
22653  * - LGPL
22654  *
22655  * TabBox
22656  * 
22657  */
22658 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22659
22660 /**
22661  * @class Roo.bootstrap.dash.TabBox
22662  * @extends Roo.bootstrap.Component
22663  * Bootstrap TabBox class
22664  * @cfg {String} title Title of the TabBox
22665  * @cfg {String} icon Icon of the TabBox
22666  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22667  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22668  * 
22669  * @constructor
22670  * Create a new TabBox
22671  * @param {Object} config The config object
22672  */
22673
22674
22675 Roo.bootstrap.dash.TabBox = function(config){
22676     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22677     this.addEvents({
22678         // raw events
22679         /**
22680          * @event addpane
22681          * When a pane is added
22682          * @param {Roo.bootstrap.dash.TabPane} pane
22683          */
22684         "addpane" : true,
22685         /**
22686          * @event activatepane
22687          * When a pane is activated
22688          * @param {Roo.bootstrap.dash.TabPane} pane
22689          */
22690         "activatepane" : true
22691         
22692          
22693     });
22694     
22695     this.panes = [];
22696 };
22697
22698 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22699
22700     title : '',
22701     icon : false,
22702     showtabs : true,
22703     tabScrollable : false,
22704     
22705     getChildContainer : function()
22706     {
22707         return this.el.select('.tab-content', true).first();
22708     },
22709     
22710     getAutoCreate : function(){
22711         
22712         var header = {
22713             tag: 'li',
22714             cls: 'pull-left header',
22715             html: this.title,
22716             cn : []
22717         };
22718         
22719         if(this.icon){
22720             header.cn.push({
22721                 tag: 'i',
22722                 cls: 'fa ' + this.icon
22723             });
22724         }
22725         
22726         var h = {
22727             tag: 'ul',
22728             cls: 'nav nav-tabs pull-right',
22729             cn: [
22730                 header
22731             ]
22732         };
22733         
22734         if(this.tabScrollable){
22735             h = {
22736                 tag: 'div',
22737                 cls: 'tab-header',
22738                 cn: [
22739                     {
22740                         tag: 'ul',
22741                         cls: 'nav nav-tabs pull-right',
22742                         cn: [
22743                             header
22744                         ]
22745                     }
22746                 ]
22747             };
22748         }
22749         
22750         var cfg = {
22751             tag: 'div',
22752             cls: 'nav-tabs-custom',
22753             cn: [
22754                 h,
22755                 {
22756                     tag: 'div',
22757                     cls: 'tab-content no-padding',
22758                     cn: []
22759                 }
22760             ]
22761         };
22762
22763         return  cfg;
22764     },
22765     initEvents : function()
22766     {
22767         //Roo.log('add add pane handler');
22768         this.on('addpane', this.onAddPane, this);
22769     },
22770      /**
22771      * Updates the box title
22772      * @param {String} html to set the title to.
22773      */
22774     setTitle : function(value)
22775     {
22776         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22777     },
22778     onAddPane : function(pane)
22779     {
22780         this.panes.push(pane);
22781         //Roo.log('addpane');
22782         //Roo.log(pane);
22783         // tabs are rendere left to right..
22784         if(!this.showtabs){
22785             return;
22786         }
22787         
22788         var ctr = this.el.select('.nav-tabs', true).first();
22789          
22790          
22791         var existing = ctr.select('.nav-tab',true);
22792         var qty = existing.getCount();;
22793         
22794         
22795         var tab = ctr.createChild({
22796             tag : 'li',
22797             cls : 'nav-tab' + (qty ? '' : ' active'),
22798             cn : [
22799                 {
22800                     tag : 'a',
22801                     href:'#',
22802                     html : pane.title
22803                 }
22804             ]
22805         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22806         pane.tab = tab;
22807         
22808         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22809         if (!qty) {
22810             pane.el.addClass('active');
22811         }
22812         
22813                 
22814     },
22815     onTabClick : function(ev,un,ob,pane)
22816     {
22817         //Roo.log('tab - prev default');
22818         ev.preventDefault();
22819         
22820         
22821         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22822         pane.tab.addClass('active');
22823         //Roo.log(pane.title);
22824         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22825         // technically we should have a deactivate event.. but maybe add later.
22826         // and it should not de-activate the selected tab...
22827         this.fireEvent('activatepane', pane);
22828         pane.el.addClass('active');
22829         pane.fireEvent('activate');
22830         
22831         
22832     },
22833     
22834     getActivePane : function()
22835     {
22836         var r = false;
22837         Roo.each(this.panes, function(p) {
22838             if(p.el.hasClass('active')){
22839                 r = p;
22840                 return false;
22841             }
22842             
22843             return;
22844         });
22845         
22846         return r;
22847     }
22848     
22849     
22850 });
22851
22852  
22853 /*
22854  * - LGPL
22855  *
22856  * Tab pane
22857  * 
22858  */
22859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22860 /**
22861  * @class Roo.bootstrap.TabPane
22862  * @extends Roo.bootstrap.Component
22863  * Bootstrap TabPane class
22864  * @cfg {Boolean} active (false | true) Default false
22865  * @cfg {String} title title of panel
22866
22867  * 
22868  * @constructor
22869  * Create a new TabPane
22870  * @param {Object} config The config object
22871  */
22872
22873 Roo.bootstrap.dash.TabPane = function(config){
22874     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22875     
22876     this.addEvents({
22877         // raw events
22878         /**
22879          * @event activate
22880          * When a pane is activated
22881          * @param {Roo.bootstrap.dash.TabPane} pane
22882          */
22883         "activate" : true
22884          
22885     });
22886 };
22887
22888 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22889     
22890     active : false,
22891     title : '',
22892     
22893     // the tabBox that this is attached to.
22894     tab : false,
22895      
22896     getAutoCreate : function() 
22897     {
22898         var cfg = {
22899             tag: 'div',
22900             cls: 'tab-pane'
22901         };
22902         
22903         if(this.active){
22904             cfg.cls += ' active';
22905         }
22906         
22907         return cfg;
22908     },
22909     initEvents  : function()
22910     {
22911         //Roo.log('trigger add pane handler');
22912         this.parent().fireEvent('addpane', this)
22913     },
22914     
22915      /**
22916      * Updates the tab title 
22917      * @param {String} html to set the title to.
22918      */
22919     setTitle: function(str)
22920     {
22921         if (!this.tab) {
22922             return;
22923         }
22924         this.title = str;
22925         this.tab.select('a', true).first().dom.innerHTML = str;
22926         
22927     }
22928     
22929     
22930     
22931 });
22932
22933  
22934
22935
22936  /*
22937  * - LGPL
22938  *
22939  * menu
22940  * 
22941  */
22942 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22943
22944 /**
22945  * @class Roo.bootstrap.menu.Menu
22946  * @extends Roo.bootstrap.Component
22947  * Bootstrap Menu class - container for Menu
22948  * @cfg {String} html Text of the menu
22949  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22950  * @cfg {String} icon Font awesome icon
22951  * @cfg {String} pos Menu align to (top | bottom) default bottom
22952  * 
22953  * 
22954  * @constructor
22955  * Create a new Menu
22956  * @param {Object} config The config object
22957  */
22958
22959
22960 Roo.bootstrap.menu.Menu = function(config){
22961     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22962     
22963     this.addEvents({
22964         /**
22965          * @event beforeshow
22966          * Fires before this menu is displayed
22967          * @param {Roo.bootstrap.menu.Menu} this
22968          */
22969         beforeshow : true,
22970         /**
22971          * @event beforehide
22972          * Fires before this menu is hidden
22973          * @param {Roo.bootstrap.menu.Menu} this
22974          */
22975         beforehide : true,
22976         /**
22977          * @event show
22978          * Fires after this menu is displayed
22979          * @param {Roo.bootstrap.menu.Menu} this
22980          */
22981         show : true,
22982         /**
22983          * @event hide
22984          * Fires after this menu is hidden
22985          * @param {Roo.bootstrap.menu.Menu} this
22986          */
22987         hide : true,
22988         /**
22989          * @event click
22990          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22991          * @param {Roo.bootstrap.menu.Menu} this
22992          * @param {Roo.EventObject} e
22993          */
22994         click : true
22995     });
22996     
22997 };
22998
22999 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23000     
23001     submenu : false,
23002     html : '',
23003     weight : 'default',
23004     icon : false,
23005     pos : 'bottom',
23006     
23007     
23008     getChildContainer : function() {
23009         if(this.isSubMenu){
23010             return this.el;
23011         }
23012         
23013         return this.el.select('ul.dropdown-menu', true).first();  
23014     },
23015     
23016     getAutoCreate : function()
23017     {
23018         var text = [
23019             {
23020                 tag : 'span',
23021                 cls : 'roo-menu-text',
23022                 html : this.html
23023             }
23024         ];
23025         
23026         if(this.icon){
23027             text.unshift({
23028                 tag : 'i',
23029                 cls : 'fa ' + this.icon
23030             })
23031         }
23032         
23033         
23034         var cfg = {
23035             tag : 'div',
23036             cls : 'btn-group',
23037             cn : [
23038                 {
23039                     tag : 'button',
23040                     cls : 'dropdown-button btn btn-' + this.weight,
23041                     cn : text
23042                 },
23043                 {
23044                     tag : 'button',
23045                     cls : 'dropdown-toggle btn btn-' + this.weight,
23046                     cn : [
23047                         {
23048                             tag : 'span',
23049                             cls : 'caret'
23050                         }
23051                     ]
23052                 },
23053                 {
23054                     tag : 'ul',
23055                     cls : 'dropdown-menu'
23056                 }
23057             ]
23058             
23059         };
23060         
23061         if(this.pos == 'top'){
23062             cfg.cls += ' dropup';
23063         }
23064         
23065         if(this.isSubMenu){
23066             cfg = {
23067                 tag : 'ul',
23068                 cls : 'dropdown-menu'
23069             }
23070         }
23071         
23072         return cfg;
23073     },
23074     
23075     onRender : function(ct, position)
23076     {
23077         this.isSubMenu = ct.hasClass('dropdown-submenu');
23078         
23079         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23080     },
23081     
23082     initEvents : function() 
23083     {
23084         if(this.isSubMenu){
23085             return;
23086         }
23087         
23088         this.hidden = true;
23089         
23090         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23091         this.triggerEl.on('click', this.onTriggerPress, this);
23092         
23093         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23094         this.buttonEl.on('click', this.onClick, this);
23095         
23096     },
23097     
23098     list : function()
23099     {
23100         if(this.isSubMenu){
23101             return this.el;
23102         }
23103         
23104         return this.el.select('ul.dropdown-menu', true).first();
23105     },
23106     
23107     onClick : function(e)
23108     {
23109         this.fireEvent("click", this, e);
23110     },
23111     
23112     onTriggerPress  : function(e)
23113     {   
23114         if (this.isVisible()) {
23115             this.hide();
23116         } else {
23117             this.show();
23118         }
23119     },
23120     
23121     isVisible : function(){
23122         return !this.hidden;
23123     },
23124     
23125     show : function()
23126     {
23127         this.fireEvent("beforeshow", this);
23128         
23129         this.hidden = false;
23130         this.el.addClass('open');
23131         
23132         Roo.get(document).on("mouseup", this.onMouseUp, this);
23133         
23134         this.fireEvent("show", this);
23135         
23136         
23137     },
23138     
23139     hide : function()
23140     {
23141         this.fireEvent("beforehide", this);
23142         
23143         this.hidden = true;
23144         this.el.removeClass('open');
23145         
23146         Roo.get(document).un("mouseup", this.onMouseUp);
23147         
23148         this.fireEvent("hide", this);
23149     },
23150     
23151     onMouseUp : function()
23152     {
23153         this.hide();
23154     }
23155     
23156 });
23157
23158  
23159  /*
23160  * - LGPL
23161  *
23162  * menu item
23163  * 
23164  */
23165 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23166
23167 /**
23168  * @class Roo.bootstrap.menu.Item
23169  * @extends Roo.bootstrap.Component
23170  * Bootstrap MenuItem class
23171  * @cfg {Boolean} submenu (true | false) default false
23172  * @cfg {String} html text of the item
23173  * @cfg {String} href the link
23174  * @cfg {Boolean} disable (true | false) default false
23175  * @cfg {Boolean} preventDefault (true | false) default true
23176  * @cfg {String} icon Font awesome icon
23177  * @cfg {String} pos Submenu align to (left | right) default right 
23178  * 
23179  * 
23180  * @constructor
23181  * Create a new Item
23182  * @param {Object} config The config object
23183  */
23184
23185
23186 Roo.bootstrap.menu.Item = function(config){
23187     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23188     this.addEvents({
23189         /**
23190          * @event mouseover
23191          * Fires when the mouse is hovering over this menu
23192          * @param {Roo.bootstrap.menu.Item} this
23193          * @param {Roo.EventObject} e
23194          */
23195         mouseover : true,
23196         /**
23197          * @event mouseout
23198          * Fires when the mouse exits this menu
23199          * @param {Roo.bootstrap.menu.Item} this
23200          * @param {Roo.EventObject} e
23201          */
23202         mouseout : true,
23203         // raw events
23204         /**
23205          * @event click
23206          * The raw click event for the entire grid.
23207          * @param {Roo.EventObject} e
23208          */
23209         click : true
23210     });
23211 };
23212
23213 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23214     
23215     submenu : false,
23216     href : '',
23217     html : '',
23218     preventDefault: true,
23219     disable : false,
23220     icon : false,
23221     pos : 'right',
23222     
23223     getAutoCreate : function()
23224     {
23225         var text = [
23226             {
23227                 tag : 'span',
23228                 cls : 'roo-menu-item-text',
23229                 html : this.html
23230             }
23231         ];
23232         
23233         if(this.icon){
23234             text.unshift({
23235                 tag : 'i',
23236                 cls : 'fa ' + this.icon
23237             })
23238         }
23239         
23240         var cfg = {
23241             tag : 'li',
23242             cn : [
23243                 {
23244                     tag : 'a',
23245                     href : this.href || '#',
23246                     cn : text
23247                 }
23248             ]
23249         };
23250         
23251         if(this.disable){
23252             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23253         }
23254         
23255         if(this.submenu){
23256             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23257             
23258             if(this.pos == 'left'){
23259                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23260             }
23261         }
23262         
23263         return cfg;
23264     },
23265     
23266     initEvents : function() 
23267     {
23268         this.el.on('mouseover', this.onMouseOver, this);
23269         this.el.on('mouseout', this.onMouseOut, this);
23270         
23271         this.el.select('a', true).first().on('click', this.onClick, this);
23272         
23273     },
23274     
23275     onClick : function(e)
23276     {
23277         if(this.preventDefault){
23278             e.preventDefault();
23279         }
23280         
23281         this.fireEvent("click", this, e);
23282     },
23283     
23284     onMouseOver : function(e)
23285     {
23286         if(this.submenu && this.pos == 'left'){
23287             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23288         }
23289         
23290         this.fireEvent("mouseover", this, e);
23291     },
23292     
23293     onMouseOut : function(e)
23294     {
23295         this.fireEvent("mouseout", this, e);
23296     }
23297 });
23298
23299  
23300
23301  /*
23302  * - LGPL
23303  *
23304  * menu separator
23305  * 
23306  */
23307 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23308
23309 /**
23310  * @class Roo.bootstrap.menu.Separator
23311  * @extends Roo.bootstrap.Component
23312  * Bootstrap Separator class
23313  * 
23314  * @constructor
23315  * Create a new Separator
23316  * @param {Object} config The config object
23317  */
23318
23319
23320 Roo.bootstrap.menu.Separator = function(config){
23321     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23322 };
23323
23324 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23325     
23326     getAutoCreate : function(){
23327         var cfg = {
23328             tag : 'li',
23329             cls: 'divider'
23330         };
23331         
23332         return cfg;
23333     }
23334    
23335 });
23336
23337  
23338
23339  /*
23340  * - LGPL
23341  *
23342  * Tooltip
23343  * 
23344  */
23345
23346 /**
23347  * @class Roo.bootstrap.Tooltip
23348  * Bootstrap Tooltip class
23349  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23350  * to determine which dom element triggers the tooltip.
23351  * 
23352  * It needs to add support for additional attributes like tooltip-position
23353  * 
23354  * @constructor
23355  * Create a new Toolti
23356  * @param {Object} config The config object
23357  */
23358
23359 Roo.bootstrap.Tooltip = function(config){
23360     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23361 };
23362
23363 Roo.apply(Roo.bootstrap.Tooltip, {
23364     /**
23365      * @function init initialize tooltip monitoring.
23366      * @static
23367      */
23368     currentEl : false,
23369     currentTip : false,
23370     currentRegion : false,
23371     
23372     //  init : delay?
23373     
23374     init : function()
23375     {
23376         Roo.get(document).on('mouseover', this.enter ,this);
23377         Roo.get(document).on('mouseout', this.leave, this);
23378          
23379         
23380         this.currentTip = new Roo.bootstrap.Tooltip();
23381     },
23382     
23383     enter : function(ev)
23384     {
23385         var dom = ev.getTarget();
23386         
23387         //Roo.log(['enter',dom]);
23388         var el = Roo.fly(dom);
23389         if (this.currentEl) {
23390             //Roo.log(dom);
23391             //Roo.log(this.currentEl);
23392             //Roo.log(this.currentEl.contains(dom));
23393             if (this.currentEl == el) {
23394                 return;
23395             }
23396             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23397                 return;
23398             }
23399
23400         }
23401         
23402         if (this.currentTip.el) {
23403             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23404         }    
23405         //Roo.log(ev);
23406         var bindEl = el;
23407         
23408         // you can not look for children, as if el is the body.. then everythign is the child..
23409         if (!el.attr('tooltip')) { //
23410             if (!el.select("[tooltip]").elements.length) {
23411                 return;
23412             }
23413             // is the mouse over this child...?
23414             bindEl = el.select("[tooltip]").first();
23415             var xy = ev.getXY();
23416             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23417                 //Roo.log("not in region.");
23418                 return;
23419             }
23420             //Roo.log("child element over..");
23421             
23422         }
23423         this.currentEl = bindEl;
23424         this.currentTip.bind(bindEl);
23425         this.currentRegion = Roo.lib.Region.getRegion(dom);
23426         this.currentTip.enter();
23427         
23428     },
23429     leave : function(ev)
23430     {
23431         var dom = ev.getTarget();
23432         //Roo.log(['leave',dom]);
23433         if (!this.currentEl) {
23434             return;
23435         }
23436         
23437         
23438         if (dom != this.currentEl.dom) {
23439             return;
23440         }
23441         var xy = ev.getXY();
23442         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23443             return;
23444         }
23445         // only activate leave if mouse cursor is outside... bounding box..
23446         
23447         
23448         
23449         
23450         if (this.currentTip) {
23451             this.currentTip.leave();
23452         }
23453         //Roo.log('clear currentEl');
23454         this.currentEl = false;
23455         
23456         
23457     },
23458     alignment : {
23459         'left' : ['r-l', [-2,0], 'right'],
23460         'right' : ['l-r', [2,0], 'left'],
23461         'bottom' : ['t-b', [0,2], 'top'],
23462         'top' : [ 'b-t', [0,-2], 'bottom']
23463     }
23464     
23465 });
23466
23467
23468 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23469     
23470     
23471     bindEl : false,
23472     
23473     delay : null, // can be { show : 300 , hide: 500}
23474     
23475     timeout : null,
23476     
23477     hoverState : null, //???
23478     
23479     placement : 'bottom', 
23480     
23481     getAutoCreate : function(){
23482     
23483         var cfg = {
23484            cls : 'tooltip',
23485            role : 'tooltip',
23486            cn : [
23487                 {
23488                     cls : 'tooltip-arrow'
23489                 },
23490                 {
23491                     cls : 'tooltip-inner'
23492                 }
23493            ]
23494         };
23495         
23496         return cfg;
23497     },
23498     bind : function(el)
23499     {
23500         this.bindEl = el;
23501     },
23502       
23503     
23504     enter : function () {
23505        
23506         if (this.timeout != null) {
23507             clearTimeout(this.timeout);
23508         }
23509         
23510         this.hoverState = 'in';
23511          //Roo.log("enter - show");
23512         if (!this.delay || !this.delay.show) {
23513             this.show();
23514             return;
23515         }
23516         var _t = this;
23517         this.timeout = setTimeout(function () {
23518             if (_t.hoverState == 'in') {
23519                 _t.show();
23520             }
23521         }, this.delay.show);
23522     },
23523     leave : function()
23524     {
23525         clearTimeout(this.timeout);
23526     
23527         this.hoverState = 'out';
23528          if (!this.delay || !this.delay.hide) {
23529             this.hide();
23530             return;
23531         }
23532        
23533         var _t = this;
23534         this.timeout = setTimeout(function () {
23535             //Roo.log("leave - timeout");
23536             
23537             if (_t.hoverState == 'out') {
23538                 _t.hide();
23539                 Roo.bootstrap.Tooltip.currentEl = false;
23540             }
23541         }, delay);
23542     },
23543     
23544     show : function ()
23545     {
23546         if (!this.el) {
23547             this.render(document.body);
23548         }
23549         // set content.
23550         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23551         
23552         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23553         
23554         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23555         
23556         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23557         
23558         var placement = typeof this.placement == 'function' ?
23559             this.placement.call(this, this.el, on_el) :
23560             this.placement;
23561             
23562         var autoToken = /\s?auto?\s?/i;
23563         var autoPlace = autoToken.test(placement);
23564         if (autoPlace) {
23565             placement = placement.replace(autoToken, '') || 'top';
23566         }
23567         
23568         //this.el.detach()
23569         //this.el.setXY([0,0]);
23570         this.el.show();
23571         //this.el.dom.style.display='block';
23572         
23573         //this.el.appendTo(on_el);
23574         
23575         var p = this.getPosition();
23576         var box = this.el.getBox();
23577         
23578         if (autoPlace) {
23579             // fixme..
23580         }
23581         
23582         var align = Roo.bootstrap.Tooltip.alignment[placement];
23583         
23584         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23585         
23586         if(placement == 'top' || placement == 'bottom'){
23587             if(xy[0] < 0){
23588                 placement = 'right';
23589             }
23590             
23591             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23592                 placement = 'left';
23593             }
23594         }
23595         
23596         align = Roo.bootstrap.Tooltip.alignment[placement];
23597         
23598         this.el.alignTo(this.bindEl, align[0],align[1]);
23599         //var arrow = this.el.select('.arrow',true).first();
23600         //arrow.set(align[2], 
23601         
23602         this.el.addClass(placement);
23603         
23604         this.el.addClass('in fade');
23605         
23606         this.hoverState = null;
23607         
23608         if (this.el.hasClass('fade')) {
23609             // fade it?
23610         }
23611         
23612     },
23613     hide : function()
23614     {
23615          
23616         if (!this.el) {
23617             return;
23618         }
23619         //this.el.setXY([0,0]);
23620         this.el.removeClass('in');
23621         //this.el.hide();
23622         
23623     }
23624     
23625 });
23626  
23627
23628  /*
23629  * - LGPL
23630  *
23631  * Location Picker
23632  * 
23633  */
23634
23635 /**
23636  * @class Roo.bootstrap.LocationPicker
23637  * @extends Roo.bootstrap.Component
23638  * Bootstrap LocationPicker class
23639  * @cfg {Number} latitude Position when init default 0
23640  * @cfg {Number} longitude Position when init default 0
23641  * @cfg {Number} zoom default 15
23642  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23643  * @cfg {Boolean} mapTypeControl default false
23644  * @cfg {Boolean} disableDoubleClickZoom default false
23645  * @cfg {Boolean} scrollwheel default true
23646  * @cfg {Boolean} streetViewControl default false
23647  * @cfg {Number} radius default 0
23648  * @cfg {String} locationName
23649  * @cfg {Boolean} draggable default true
23650  * @cfg {Boolean} enableAutocomplete default false
23651  * @cfg {Boolean} enableReverseGeocode default true
23652  * @cfg {String} markerTitle
23653  * 
23654  * @constructor
23655  * Create a new LocationPicker
23656  * @param {Object} config The config object
23657  */
23658
23659
23660 Roo.bootstrap.LocationPicker = function(config){
23661     
23662     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23663     
23664     this.addEvents({
23665         /**
23666          * @event initial
23667          * Fires when the picker initialized.
23668          * @param {Roo.bootstrap.LocationPicker} this
23669          * @param {Google Location} location
23670          */
23671         initial : true,
23672         /**
23673          * @event positionchanged
23674          * Fires when the picker position changed.
23675          * @param {Roo.bootstrap.LocationPicker} this
23676          * @param {Google Location} location
23677          */
23678         positionchanged : true,
23679         /**
23680          * @event resize
23681          * Fires when the map resize.
23682          * @param {Roo.bootstrap.LocationPicker} this
23683          */
23684         resize : true,
23685         /**
23686          * @event show
23687          * Fires when the map show.
23688          * @param {Roo.bootstrap.LocationPicker} this
23689          */
23690         show : true,
23691         /**
23692          * @event hide
23693          * Fires when the map hide.
23694          * @param {Roo.bootstrap.LocationPicker} this
23695          */
23696         hide : true,
23697         /**
23698          * @event mapClick
23699          * Fires when click the map.
23700          * @param {Roo.bootstrap.LocationPicker} this
23701          * @param {Map event} e
23702          */
23703         mapClick : true,
23704         /**
23705          * @event mapRightClick
23706          * Fires when right click the map.
23707          * @param {Roo.bootstrap.LocationPicker} this
23708          * @param {Map event} e
23709          */
23710         mapRightClick : true,
23711         /**
23712          * @event markerClick
23713          * Fires when click the marker.
23714          * @param {Roo.bootstrap.LocationPicker} this
23715          * @param {Map event} e
23716          */
23717         markerClick : true,
23718         /**
23719          * @event markerRightClick
23720          * Fires when right click the marker.
23721          * @param {Roo.bootstrap.LocationPicker} this
23722          * @param {Map event} e
23723          */
23724         markerRightClick : true,
23725         /**
23726          * @event OverlayViewDraw
23727          * Fires when OverlayView Draw
23728          * @param {Roo.bootstrap.LocationPicker} this
23729          */
23730         OverlayViewDraw : true,
23731         /**
23732          * @event OverlayViewOnAdd
23733          * Fires when OverlayView Draw
23734          * @param {Roo.bootstrap.LocationPicker} this
23735          */
23736         OverlayViewOnAdd : true,
23737         /**
23738          * @event OverlayViewOnRemove
23739          * Fires when OverlayView Draw
23740          * @param {Roo.bootstrap.LocationPicker} this
23741          */
23742         OverlayViewOnRemove : true,
23743         /**
23744          * @event OverlayViewShow
23745          * Fires when OverlayView Draw
23746          * @param {Roo.bootstrap.LocationPicker} this
23747          * @param {Pixel} cpx
23748          */
23749         OverlayViewShow : true,
23750         /**
23751          * @event OverlayViewHide
23752          * Fires when OverlayView Draw
23753          * @param {Roo.bootstrap.LocationPicker} this
23754          */
23755         OverlayViewHide : true,
23756         /**
23757          * @event loadexception
23758          * Fires when load google lib failed.
23759          * @param {Roo.bootstrap.LocationPicker} this
23760          */
23761         loadexception : true
23762     });
23763         
23764 };
23765
23766 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23767     
23768     gMapContext: false,
23769     
23770     latitude: 0,
23771     longitude: 0,
23772     zoom: 15,
23773     mapTypeId: false,
23774     mapTypeControl: false,
23775     disableDoubleClickZoom: false,
23776     scrollwheel: true,
23777     streetViewControl: false,
23778     radius: 0,
23779     locationName: '',
23780     draggable: true,
23781     enableAutocomplete: false,
23782     enableReverseGeocode: true,
23783     markerTitle: '',
23784     
23785     getAutoCreate: function()
23786     {
23787
23788         var cfg = {
23789             tag: 'div',
23790             cls: 'roo-location-picker'
23791         };
23792         
23793         return cfg
23794     },
23795     
23796     initEvents: function(ct, position)
23797     {       
23798         if(!this.el.getWidth() || this.isApplied()){
23799             return;
23800         }
23801         
23802         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23803         
23804         this.initial();
23805     },
23806     
23807     initial: function()
23808     {
23809         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23810             this.fireEvent('loadexception', this);
23811             return;
23812         }
23813         
23814         if(!this.mapTypeId){
23815             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23816         }
23817         
23818         this.gMapContext = this.GMapContext();
23819         
23820         this.initOverlayView();
23821         
23822         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23823         
23824         var _this = this;
23825                 
23826         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23827             _this.setPosition(_this.gMapContext.marker.position);
23828         });
23829         
23830         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23831             _this.fireEvent('mapClick', this, event);
23832             
23833         });
23834
23835         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23836             _this.fireEvent('mapRightClick', this, event);
23837             
23838         });
23839         
23840         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23841             _this.fireEvent('markerClick', this, event);
23842             
23843         });
23844
23845         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23846             _this.fireEvent('markerRightClick', this, event);
23847             
23848         });
23849         
23850         this.setPosition(this.gMapContext.location);
23851         
23852         this.fireEvent('initial', this, this.gMapContext.location);
23853     },
23854     
23855     initOverlayView: function()
23856     {
23857         var _this = this;
23858         
23859         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23860             
23861             draw: function()
23862             {
23863                 _this.fireEvent('OverlayViewDraw', _this);
23864             },
23865             
23866             onAdd: function()
23867             {
23868                 _this.fireEvent('OverlayViewOnAdd', _this);
23869             },
23870             
23871             onRemove: function()
23872             {
23873                 _this.fireEvent('OverlayViewOnRemove', _this);
23874             },
23875             
23876             show: function(cpx)
23877             {
23878                 _this.fireEvent('OverlayViewShow', _this, cpx);
23879             },
23880             
23881             hide: function()
23882             {
23883                 _this.fireEvent('OverlayViewHide', _this);
23884             }
23885             
23886         });
23887     },
23888     
23889     fromLatLngToContainerPixel: function(event)
23890     {
23891         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23892     },
23893     
23894     isApplied: function() 
23895     {
23896         return this.getGmapContext() == false ? false : true;
23897     },
23898     
23899     getGmapContext: function() 
23900     {
23901         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23902     },
23903     
23904     GMapContext: function() 
23905     {
23906         var position = new google.maps.LatLng(this.latitude, this.longitude);
23907         
23908         var _map = new google.maps.Map(this.el.dom, {
23909             center: position,
23910             zoom: this.zoom,
23911             mapTypeId: this.mapTypeId,
23912             mapTypeControl: this.mapTypeControl,
23913             disableDoubleClickZoom: this.disableDoubleClickZoom,
23914             scrollwheel: this.scrollwheel,
23915             streetViewControl: this.streetViewControl,
23916             locationName: this.locationName,
23917             draggable: this.draggable,
23918             enableAutocomplete: this.enableAutocomplete,
23919             enableReverseGeocode: this.enableReverseGeocode
23920         });
23921         
23922         var _marker = new google.maps.Marker({
23923             position: position,
23924             map: _map,
23925             title: this.markerTitle,
23926             draggable: this.draggable
23927         });
23928         
23929         return {
23930             map: _map,
23931             marker: _marker,
23932             circle: null,
23933             location: position,
23934             radius: this.radius,
23935             locationName: this.locationName,
23936             addressComponents: {
23937                 formatted_address: null,
23938                 addressLine1: null,
23939                 addressLine2: null,
23940                 streetName: null,
23941                 streetNumber: null,
23942                 city: null,
23943                 district: null,
23944                 state: null,
23945                 stateOrProvince: null
23946             },
23947             settings: this,
23948             domContainer: this.el.dom,
23949             geodecoder: new google.maps.Geocoder()
23950         };
23951     },
23952     
23953     drawCircle: function(center, radius, options) 
23954     {
23955         if (this.gMapContext.circle != null) {
23956             this.gMapContext.circle.setMap(null);
23957         }
23958         if (radius > 0) {
23959             radius *= 1;
23960             options = Roo.apply({}, options, {
23961                 strokeColor: "#0000FF",
23962                 strokeOpacity: .35,
23963                 strokeWeight: 2,
23964                 fillColor: "#0000FF",
23965                 fillOpacity: .2
23966             });
23967             
23968             options.map = this.gMapContext.map;
23969             options.radius = radius;
23970             options.center = center;
23971             this.gMapContext.circle = new google.maps.Circle(options);
23972             return this.gMapContext.circle;
23973         }
23974         
23975         return null;
23976     },
23977     
23978     setPosition: function(location) 
23979     {
23980         this.gMapContext.location = location;
23981         this.gMapContext.marker.setPosition(location);
23982         this.gMapContext.map.panTo(location);
23983         this.drawCircle(location, this.gMapContext.radius, {});
23984         
23985         var _this = this;
23986         
23987         if (this.gMapContext.settings.enableReverseGeocode) {
23988             this.gMapContext.geodecoder.geocode({
23989                 latLng: this.gMapContext.location
23990             }, function(results, status) {
23991                 
23992                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23993                     _this.gMapContext.locationName = results[0].formatted_address;
23994                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23995                     
23996                     _this.fireEvent('positionchanged', this, location);
23997                 }
23998             });
23999             
24000             return;
24001         }
24002         
24003         this.fireEvent('positionchanged', this, location);
24004     },
24005     
24006     resize: function()
24007     {
24008         google.maps.event.trigger(this.gMapContext.map, "resize");
24009         
24010         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24011         
24012         this.fireEvent('resize', this);
24013     },
24014     
24015     setPositionByLatLng: function(latitude, longitude)
24016     {
24017         this.setPosition(new google.maps.LatLng(latitude, longitude));
24018     },
24019     
24020     getCurrentPosition: function() 
24021     {
24022         return {
24023             latitude: this.gMapContext.location.lat(),
24024             longitude: this.gMapContext.location.lng()
24025         };
24026     },
24027     
24028     getAddressName: function() 
24029     {
24030         return this.gMapContext.locationName;
24031     },
24032     
24033     getAddressComponents: function() 
24034     {
24035         return this.gMapContext.addressComponents;
24036     },
24037     
24038     address_component_from_google_geocode: function(address_components) 
24039     {
24040         var result = {};
24041         
24042         for (var i = 0; i < address_components.length; i++) {
24043             var component = address_components[i];
24044             if (component.types.indexOf("postal_code") >= 0) {
24045                 result.postalCode = component.short_name;
24046             } else if (component.types.indexOf("street_number") >= 0) {
24047                 result.streetNumber = component.short_name;
24048             } else if (component.types.indexOf("route") >= 0) {
24049                 result.streetName = component.short_name;
24050             } else if (component.types.indexOf("neighborhood") >= 0) {
24051                 result.city = component.short_name;
24052             } else if (component.types.indexOf("locality") >= 0) {
24053                 result.city = component.short_name;
24054             } else if (component.types.indexOf("sublocality") >= 0) {
24055                 result.district = component.short_name;
24056             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24057                 result.stateOrProvince = component.short_name;
24058             } else if (component.types.indexOf("country") >= 0) {
24059                 result.country = component.short_name;
24060             }
24061         }
24062         
24063         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24064         result.addressLine2 = "";
24065         return result;
24066     },
24067     
24068     setZoomLevel: function(zoom)
24069     {
24070         this.gMapContext.map.setZoom(zoom);
24071     },
24072     
24073     show: function()
24074     {
24075         if(!this.el){
24076             return;
24077         }
24078         
24079         this.el.show();
24080         
24081         this.resize();
24082         
24083         this.fireEvent('show', this);
24084     },
24085     
24086     hide: function()
24087     {
24088         if(!this.el){
24089             return;
24090         }
24091         
24092         this.el.hide();
24093         
24094         this.fireEvent('hide', this);
24095     }
24096     
24097 });
24098
24099 Roo.apply(Roo.bootstrap.LocationPicker, {
24100     
24101     OverlayView : function(map, options)
24102     {
24103         options = options || {};
24104         
24105         this.setMap(map);
24106     }
24107     
24108     
24109 });/*
24110  * - LGPL
24111  *
24112  * Alert
24113  * 
24114  */
24115
24116 /**
24117  * @class Roo.bootstrap.Alert
24118  * @extends Roo.bootstrap.Component
24119  * Bootstrap Alert class
24120  * @cfg {String} title The title of alert
24121  * @cfg {String} html The content of alert
24122  * @cfg {String} weight (  success | info | warning | danger )
24123  * @cfg {String} faicon font-awesomeicon
24124  * 
24125  * @constructor
24126  * Create a new alert
24127  * @param {Object} config The config object
24128  */
24129
24130
24131 Roo.bootstrap.Alert = function(config){
24132     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24133     
24134 };
24135
24136 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24137     
24138     title: '',
24139     html: '',
24140     weight: false,
24141     faicon: false,
24142     
24143     getAutoCreate : function()
24144     {
24145         
24146         var cfg = {
24147             tag : 'div',
24148             cls : 'alert',
24149             cn : [
24150                 {
24151                     tag : 'i',
24152                     cls : 'roo-alert-icon'
24153                     
24154                 },
24155                 {
24156                     tag : 'b',
24157                     cls : 'roo-alert-title',
24158                     html : this.title
24159                 },
24160                 {
24161                     tag : 'span',
24162                     cls : 'roo-alert-text',
24163                     html : this.html
24164                 }
24165             ]
24166         };
24167         
24168         if(this.faicon){
24169             cfg.cn[0].cls += ' fa ' + this.faicon;
24170         }
24171         
24172         if(this.weight){
24173             cfg.cls += ' alert-' + this.weight;
24174         }
24175         
24176         return cfg;
24177     },
24178     
24179     initEvents: function() 
24180     {
24181         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24182     },
24183     
24184     setTitle : function(str)
24185     {
24186         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24187     },
24188     
24189     setText : function(str)
24190     {
24191         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24192     },
24193     
24194     setWeight : function(weight)
24195     {
24196         if(this.weight){
24197             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24198         }
24199         
24200         this.weight = weight;
24201         
24202         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24203     },
24204     
24205     setIcon : function(icon)
24206     {
24207         if(this.faicon){
24208             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24209         }
24210         
24211         this.faicon = icon;
24212         
24213         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24214     },
24215     
24216     hide: function() 
24217     {
24218         this.el.hide();   
24219     },
24220     
24221     show: function() 
24222     {  
24223         this.el.show();   
24224     }
24225     
24226 });
24227
24228  
24229 /*
24230 * Licence: LGPL
24231 */
24232
24233 /**
24234  * @class Roo.bootstrap.UploadCropbox
24235  * @extends Roo.bootstrap.Component
24236  * Bootstrap UploadCropbox class
24237  * @cfg {String} emptyText show when image has been loaded
24238  * @cfg {String} rotateNotify show when image too small to rotate
24239  * @cfg {Number} errorTimeout default 3000
24240  * @cfg {Number} minWidth default 300
24241  * @cfg {Number} minHeight default 300
24242  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24243  * @cfg {Boolean} isDocument (true|false) default false
24244  * @cfg {String} url action url
24245  * @cfg {String} paramName default 'imageUpload'
24246  * @cfg {String} method default POST
24247  * @cfg {Boolean} loadMask (true|false) default true
24248  * @cfg {Boolean} loadingText default 'Loading...'
24249  * 
24250  * @constructor
24251  * Create a new UploadCropbox
24252  * @param {Object} config The config object
24253  */
24254
24255 Roo.bootstrap.UploadCropbox = function(config){
24256     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24257     
24258     this.addEvents({
24259         /**
24260          * @event beforeselectfile
24261          * Fire before select file
24262          * @param {Roo.bootstrap.UploadCropbox} this
24263          */
24264         "beforeselectfile" : true,
24265         /**
24266          * @event initial
24267          * Fire after initEvent
24268          * @param {Roo.bootstrap.UploadCropbox} this
24269          */
24270         "initial" : true,
24271         /**
24272          * @event crop
24273          * Fire after initEvent
24274          * @param {Roo.bootstrap.UploadCropbox} this
24275          * @param {String} data
24276          */
24277         "crop" : true,
24278         /**
24279          * @event prepare
24280          * Fire when preparing the file data
24281          * @param {Roo.bootstrap.UploadCropbox} this
24282          * @param {Object} file
24283          */
24284         "prepare" : true,
24285         /**
24286          * @event exception
24287          * Fire when get exception
24288          * @param {Roo.bootstrap.UploadCropbox} this
24289          * @param {XMLHttpRequest} xhr
24290          */
24291         "exception" : true,
24292         /**
24293          * @event beforeloadcanvas
24294          * Fire before load the canvas
24295          * @param {Roo.bootstrap.UploadCropbox} this
24296          * @param {String} src
24297          */
24298         "beforeloadcanvas" : true,
24299         /**
24300          * @event trash
24301          * Fire when trash image
24302          * @param {Roo.bootstrap.UploadCropbox} this
24303          */
24304         "trash" : true,
24305         /**
24306          * @event download
24307          * Fire when download the image
24308          * @param {Roo.bootstrap.UploadCropbox} this
24309          */
24310         "download" : true,
24311         /**
24312          * @event footerbuttonclick
24313          * Fire when footerbuttonclick
24314          * @param {Roo.bootstrap.UploadCropbox} this
24315          * @param {String} type
24316          */
24317         "footerbuttonclick" : true,
24318         /**
24319          * @event resize
24320          * Fire when resize
24321          * @param {Roo.bootstrap.UploadCropbox} this
24322          */
24323         "resize" : true,
24324         /**
24325          * @event rotate
24326          * Fire when rotate the image
24327          * @param {Roo.bootstrap.UploadCropbox} this
24328          * @param {String} pos
24329          */
24330         "rotate" : true,
24331         /**
24332          * @event inspect
24333          * Fire when inspect the file
24334          * @param {Roo.bootstrap.UploadCropbox} this
24335          * @param {Object} file
24336          */
24337         "inspect" : true,
24338         /**
24339          * @event upload
24340          * Fire when xhr upload the file
24341          * @param {Roo.bootstrap.UploadCropbox} this
24342          * @param {Object} data
24343          */
24344         "upload" : true,
24345         /**
24346          * @event arrange
24347          * Fire when arrange the file data
24348          * @param {Roo.bootstrap.UploadCropbox} this
24349          * @param {Object} formData
24350          */
24351         "arrange" : true
24352     });
24353     
24354     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24355 };
24356
24357 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24358     
24359     emptyText : 'Click to upload image',
24360     rotateNotify : 'Image is too small to rotate',
24361     errorTimeout : 3000,
24362     scale : 0,
24363     baseScale : 1,
24364     rotate : 0,
24365     dragable : false,
24366     pinching : false,
24367     mouseX : 0,
24368     mouseY : 0,
24369     cropData : false,
24370     minWidth : 300,
24371     minHeight : 300,
24372     file : false,
24373     exif : {},
24374     baseRotate : 1,
24375     cropType : 'image/jpeg',
24376     buttons : false,
24377     canvasLoaded : false,
24378     isDocument : false,
24379     method : 'POST',
24380     paramName : 'imageUpload',
24381     loadMask : true,
24382     loadingText : 'Loading...',
24383     maskEl : false,
24384     
24385     getAutoCreate : function()
24386     {
24387         var cfg = {
24388             tag : 'div',
24389             cls : 'roo-upload-cropbox',
24390             cn : [
24391                 {
24392                     tag : 'input',
24393                     cls : 'roo-upload-cropbox-selector',
24394                     type : 'file'
24395                 },
24396                 {
24397                     tag : 'div',
24398                     cls : 'roo-upload-cropbox-body',
24399                     style : 'cursor:pointer',
24400                     cn : [
24401                         {
24402                             tag : 'div',
24403                             cls : 'roo-upload-cropbox-preview'
24404                         },
24405                         {
24406                             tag : 'div',
24407                             cls : 'roo-upload-cropbox-thumb'
24408                         },
24409                         {
24410                             tag : 'div',
24411                             cls : 'roo-upload-cropbox-empty-notify',
24412                             html : this.emptyText
24413                         },
24414                         {
24415                             tag : 'div',
24416                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24417                             html : this.rotateNotify
24418                         }
24419                     ]
24420                 },
24421                 {
24422                     tag : 'div',
24423                     cls : 'roo-upload-cropbox-footer',
24424                     cn : {
24425                         tag : 'div',
24426                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24427                         cn : []
24428                     }
24429                 }
24430             ]
24431         };
24432         
24433         return cfg;
24434     },
24435     
24436     onRender : function(ct, position)
24437     {
24438         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24439         
24440         if (this.buttons.length) {
24441             
24442             Roo.each(this.buttons, function(bb) {
24443                 
24444                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24445                 
24446                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24447                 
24448             }, this);
24449         }
24450         
24451         if(this.loadMask){
24452             this.maskEl = this.el;
24453         }
24454     },
24455     
24456     initEvents : function()
24457     {
24458         this.urlAPI = (window.createObjectURL && window) || 
24459                                 (window.URL && URL.revokeObjectURL && URL) || 
24460                                 (window.webkitURL && webkitURL);
24461                         
24462         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24463         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24464         
24465         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24466         this.selectorEl.hide();
24467         
24468         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24469         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24470         
24471         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24472         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24473         this.thumbEl.hide();
24474         
24475         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24476         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24477         
24478         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24479         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24480         this.errorEl.hide();
24481         
24482         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24483         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24484         this.footerEl.hide();
24485         
24486         this.setThumbBoxSize();
24487         
24488         this.bind();
24489         
24490         this.resize();
24491         
24492         this.fireEvent('initial', this);
24493     },
24494
24495     bind : function()
24496     {
24497         var _this = this;
24498         
24499         window.addEventListener("resize", function() { _this.resize(); } );
24500         
24501         this.bodyEl.on('click', this.beforeSelectFile, this);
24502         
24503         if(Roo.isTouch){
24504             this.bodyEl.on('touchstart', this.onTouchStart, this);
24505             this.bodyEl.on('touchmove', this.onTouchMove, this);
24506             this.bodyEl.on('touchend', this.onTouchEnd, this);
24507         }
24508         
24509         if(!Roo.isTouch){
24510             this.bodyEl.on('mousedown', this.onMouseDown, this);
24511             this.bodyEl.on('mousemove', this.onMouseMove, this);
24512             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24513             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24514             Roo.get(document).on('mouseup', this.onMouseUp, this);
24515         }
24516         
24517         this.selectorEl.on('change', this.onFileSelected, this);
24518     },
24519     
24520     reset : function()
24521     {    
24522         this.scale = 0;
24523         this.baseScale = 1;
24524         this.rotate = 0;
24525         this.baseRotate = 1;
24526         this.dragable = false;
24527         this.pinching = false;
24528         this.mouseX = 0;
24529         this.mouseY = 0;
24530         this.cropData = false;
24531         this.notifyEl.dom.innerHTML = this.emptyText;
24532         
24533         this.selectorEl.dom.value = '';
24534         
24535     },
24536     
24537     resize : function()
24538     {
24539         if(this.fireEvent('resize', this) != false){
24540             this.setThumbBoxPosition();
24541             this.setCanvasPosition();
24542         }
24543     },
24544     
24545     onFooterButtonClick : function(e, el, o, type)
24546     {
24547         switch (type) {
24548             case 'rotate-left' :
24549                 this.onRotateLeft(e);
24550                 break;
24551             case 'rotate-right' :
24552                 this.onRotateRight(e);
24553                 break;
24554             case 'picture' :
24555                 this.beforeSelectFile(e);
24556                 break;
24557             case 'trash' :
24558                 this.trash(e);
24559                 break;
24560             case 'crop' :
24561                 this.crop(e);
24562                 break;
24563             case 'download' :
24564                 this.download(e);
24565                 break;
24566             default :
24567                 break;
24568         }
24569         
24570         this.fireEvent('footerbuttonclick', this, type);
24571     },
24572     
24573     beforeSelectFile : function(e)
24574     {
24575         e.preventDefault();
24576         
24577         if(this.fireEvent('beforeselectfile', this) != false){
24578             this.selectorEl.dom.click();
24579         }
24580     },
24581     
24582     onFileSelected : function(e)
24583     {
24584         e.preventDefault();
24585         
24586         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24587             return;
24588         }
24589         
24590         var file = this.selectorEl.dom.files[0];
24591         
24592         if(this.fireEvent('inspect', this, file) != false){
24593             this.prepare(file);
24594         }
24595         
24596     },
24597     
24598     trash : function(e)
24599     {
24600         this.fireEvent('trash', this);
24601     },
24602     
24603     download : function(e)
24604     {
24605         this.fireEvent('download', this);
24606     },
24607     
24608     loadCanvas : function(src)
24609     {   
24610         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24611             
24612             this.reset();
24613             
24614             this.imageEl = document.createElement('img');
24615             
24616             var _this = this;
24617             
24618             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24619             
24620             this.imageEl.src = src;
24621         }
24622     },
24623     
24624     onLoadCanvas : function()
24625     {   
24626         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24627         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24628         
24629         this.bodyEl.un('click', this.beforeSelectFile, this);
24630         
24631         this.notifyEl.hide();
24632         this.thumbEl.show();
24633         this.footerEl.show();
24634         
24635         this.baseRotateLevel();
24636         
24637         if(this.isDocument){
24638             this.setThumbBoxSize();
24639         }
24640         
24641         this.setThumbBoxPosition();
24642         
24643         this.baseScaleLevel();
24644         
24645         this.draw();
24646         
24647         this.resize();
24648         
24649         this.canvasLoaded = true;
24650         
24651         if(this.loadMask){
24652             this.maskEl.unmask();
24653         }
24654         
24655     },
24656     
24657     setCanvasPosition : function()
24658     {   
24659         if(!this.canvasEl){
24660             return;
24661         }
24662         
24663         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24664         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24665         
24666         this.previewEl.setLeft(pw);
24667         this.previewEl.setTop(ph);
24668         
24669     },
24670     
24671     onMouseDown : function(e)
24672     {   
24673         e.stopEvent();
24674         
24675         this.dragable = true;
24676         this.pinching = false;
24677         
24678         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24679             this.dragable = false;
24680             return;
24681         }
24682         
24683         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24684         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24685         
24686     },
24687     
24688     onMouseMove : function(e)
24689     {   
24690         e.stopEvent();
24691         
24692         if(!this.canvasLoaded){
24693             return;
24694         }
24695         
24696         if (!this.dragable){
24697             return;
24698         }
24699         
24700         var minX = Math.ceil(this.thumbEl.getLeft(true));
24701         var minY = Math.ceil(this.thumbEl.getTop(true));
24702         
24703         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24704         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24705         
24706         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24707         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24708         
24709         x = x - this.mouseX;
24710         y = y - this.mouseY;
24711         
24712         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24713         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24714         
24715         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24716         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24717         
24718         this.previewEl.setLeft(bgX);
24719         this.previewEl.setTop(bgY);
24720         
24721         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24722         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24723     },
24724     
24725     onMouseUp : function(e)
24726     {   
24727         e.stopEvent();
24728         
24729         this.dragable = false;
24730     },
24731     
24732     onMouseWheel : function(e)
24733     {   
24734         e.stopEvent();
24735         
24736         this.startScale = this.scale;
24737         
24738         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24739         
24740         if(!this.zoomable()){
24741             this.scale = this.startScale;
24742             return;
24743         }
24744         
24745         this.draw();
24746         
24747         return;
24748     },
24749     
24750     zoomable : function()
24751     {
24752         var minScale = this.thumbEl.getWidth() / this.minWidth;
24753         
24754         if(this.minWidth < this.minHeight){
24755             minScale = this.thumbEl.getHeight() / this.minHeight;
24756         }
24757         
24758         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24759         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24760         
24761         if(
24762                 this.isDocument &&
24763                 (this.rotate == 0 || this.rotate == 180) && 
24764                 (
24765                     width > this.imageEl.OriginWidth || 
24766                     height > this.imageEl.OriginHeight ||
24767                     (width < this.minWidth && height < this.minHeight)
24768                 )
24769         ){
24770             return false;
24771         }
24772         
24773         if(
24774                 this.isDocument &&
24775                 (this.rotate == 90 || this.rotate == 270) && 
24776                 (
24777                     width > this.imageEl.OriginWidth || 
24778                     height > this.imageEl.OriginHeight ||
24779                     (width < this.minHeight && height < this.minWidth)
24780                 )
24781         ){
24782             return false;
24783         }
24784         
24785         if(
24786                 !this.isDocument &&
24787                 (this.rotate == 0 || this.rotate == 180) && 
24788                 (
24789                     width < this.minWidth || 
24790                     width > this.imageEl.OriginWidth || 
24791                     height < this.minHeight || 
24792                     height > this.imageEl.OriginHeight
24793                 )
24794         ){
24795             return false;
24796         }
24797         
24798         if(
24799                 !this.isDocument &&
24800                 (this.rotate == 90 || this.rotate == 270) && 
24801                 (
24802                     width < this.minHeight || 
24803                     width > this.imageEl.OriginWidth || 
24804                     height < this.minWidth || 
24805                     height > this.imageEl.OriginHeight
24806                 )
24807         ){
24808             return false;
24809         }
24810         
24811         return true;
24812         
24813     },
24814     
24815     onRotateLeft : function(e)
24816     {   
24817         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24818             
24819             var minScale = this.thumbEl.getWidth() / this.minWidth;
24820             
24821             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24822             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24823             
24824             this.startScale = this.scale;
24825             
24826             while (this.getScaleLevel() < minScale){
24827             
24828                 this.scale = this.scale + 1;
24829                 
24830                 if(!this.zoomable()){
24831                     break;
24832                 }
24833                 
24834                 if(
24835                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24836                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24837                 ){
24838                     continue;
24839                 }
24840                 
24841                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24842
24843                 this.draw();
24844                 
24845                 return;
24846             }
24847             
24848             this.scale = this.startScale;
24849             
24850             this.onRotateFail();
24851             
24852             return false;
24853         }
24854         
24855         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24856
24857         if(this.isDocument){
24858             this.setThumbBoxSize();
24859             this.setThumbBoxPosition();
24860             this.setCanvasPosition();
24861         }
24862         
24863         this.draw();
24864         
24865         this.fireEvent('rotate', this, 'left');
24866         
24867     },
24868     
24869     onRotateRight : function(e)
24870     {
24871         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24872             
24873             var minScale = this.thumbEl.getWidth() / this.minWidth;
24874         
24875             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24876             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24877             
24878             this.startScale = this.scale;
24879             
24880             while (this.getScaleLevel() < minScale){
24881             
24882                 this.scale = this.scale + 1;
24883                 
24884                 if(!this.zoomable()){
24885                     break;
24886                 }
24887                 
24888                 if(
24889                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24890                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24891                 ){
24892                     continue;
24893                 }
24894                 
24895                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24896
24897                 this.draw();
24898                 
24899                 return;
24900             }
24901             
24902             this.scale = this.startScale;
24903             
24904             this.onRotateFail();
24905             
24906             return false;
24907         }
24908         
24909         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24910
24911         if(this.isDocument){
24912             this.setThumbBoxSize();
24913             this.setThumbBoxPosition();
24914             this.setCanvasPosition();
24915         }
24916         
24917         this.draw();
24918         
24919         this.fireEvent('rotate', this, 'right');
24920     },
24921     
24922     onRotateFail : function()
24923     {
24924         this.errorEl.show(true);
24925         
24926         var _this = this;
24927         
24928         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24929     },
24930     
24931     draw : function()
24932     {
24933         this.previewEl.dom.innerHTML = '';
24934         
24935         var canvasEl = document.createElement("canvas");
24936         
24937         var contextEl = canvasEl.getContext("2d");
24938         
24939         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24940         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24941         var center = this.imageEl.OriginWidth / 2;
24942         
24943         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24944             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24945             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24946             center = this.imageEl.OriginHeight / 2;
24947         }
24948         
24949         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24950         
24951         contextEl.translate(center, center);
24952         contextEl.rotate(this.rotate * Math.PI / 180);
24953
24954         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24955         
24956         this.canvasEl = document.createElement("canvas");
24957         
24958         this.contextEl = this.canvasEl.getContext("2d");
24959         
24960         switch (this.rotate) {
24961             case 0 :
24962                 
24963                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24964                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24965                 
24966                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24967                 
24968                 break;
24969             case 90 : 
24970                 
24971                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24972                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24973                 
24974                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24975                     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);
24976                     break;
24977                 }
24978                 
24979                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24980                 
24981                 break;
24982             case 180 :
24983                 
24984                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24985                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24986                 
24987                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24988                     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);
24989                     break;
24990                 }
24991                 
24992                 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);
24993                 
24994                 break;
24995             case 270 :
24996                 
24997                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24998                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24999         
25000                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25001                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25002                     break;
25003                 }
25004                 
25005                 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);
25006                 
25007                 break;
25008             default : 
25009                 break;
25010         }
25011         
25012         this.previewEl.appendChild(this.canvasEl);
25013         
25014         this.setCanvasPosition();
25015     },
25016     
25017     crop : function()
25018     {
25019         if(!this.canvasLoaded){
25020             return;
25021         }
25022         
25023         var imageCanvas = document.createElement("canvas");
25024         
25025         var imageContext = imageCanvas.getContext("2d");
25026         
25027         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25028         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25029         
25030         var center = imageCanvas.width / 2;
25031         
25032         imageContext.translate(center, center);
25033         
25034         imageContext.rotate(this.rotate * Math.PI / 180);
25035         
25036         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25037         
25038         var canvas = document.createElement("canvas");
25039         
25040         var context = canvas.getContext("2d");
25041                 
25042         canvas.width = this.minWidth;
25043         canvas.height = this.minHeight;
25044
25045         switch (this.rotate) {
25046             case 0 :
25047                 
25048                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25049                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25050                 
25051                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25052                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25053                 
25054                 var targetWidth = this.minWidth - 2 * x;
25055                 var targetHeight = this.minHeight - 2 * y;
25056                 
25057                 var scale = 1;
25058                 
25059                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25060                     scale = targetWidth / width;
25061                 }
25062                 
25063                 if(x > 0 && y == 0){
25064                     scale = targetHeight / height;
25065                 }
25066                 
25067                 if(x > 0 && y > 0){
25068                     scale = targetWidth / width;
25069                     
25070                     if(width < height){
25071                         scale = targetHeight / height;
25072                     }
25073                 }
25074                 
25075                 context.scale(scale, scale);
25076                 
25077                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25078                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25079
25080                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25081                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25082
25083                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25084                 
25085                 break;
25086             case 90 : 
25087                 
25088                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25089                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25090                 
25091                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25092                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25093                 
25094                 var targetWidth = this.minWidth - 2 * x;
25095                 var targetHeight = this.minHeight - 2 * y;
25096                 
25097                 var scale = 1;
25098                 
25099                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25100                     scale = targetWidth / width;
25101                 }
25102                 
25103                 if(x > 0 && y == 0){
25104                     scale = targetHeight / height;
25105                 }
25106                 
25107                 if(x > 0 && y > 0){
25108                     scale = targetWidth / width;
25109                     
25110                     if(width < height){
25111                         scale = targetHeight / height;
25112                     }
25113                 }
25114                 
25115                 context.scale(scale, scale);
25116                 
25117                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25118                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25119
25120                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25121                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25122                 
25123                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25124                 
25125                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25126                 
25127                 break;
25128             case 180 :
25129                 
25130                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25131                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25132                 
25133                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25134                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25135                 
25136                 var targetWidth = this.minWidth - 2 * x;
25137                 var targetHeight = this.minHeight - 2 * y;
25138                 
25139                 var scale = 1;
25140                 
25141                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25142                     scale = targetWidth / width;
25143                 }
25144                 
25145                 if(x > 0 && y == 0){
25146                     scale = targetHeight / height;
25147                 }
25148                 
25149                 if(x > 0 && y > 0){
25150                     scale = targetWidth / width;
25151                     
25152                     if(width < height){
25153                         scale = targetHeight / height;
25154                     }
25155                 }
25156                 
25157                 context.scale(scale, scale);
25158                 
25159                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25160                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25161
25162                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25163                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25164
25165                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25166                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25167                 
25168                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25169                 
25170                 break;
25171             case 270 :
25172                 
25173                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25174                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25175                 
25176                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25177                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25178                 
25179                 var targetWidth = this.minWidth - 2 * x;
25180                 var targetHeight = this.minHeight - 2 * y;
25181                 
25182                 var scale = 1;
25183                 
25184                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25185                     scale = targetWidth / width;
25186                 }
25187                 
25188                 if(x > 0 && y == 0){
25189                     scale = targetHeight / height;
25190                 }
25191                 
25192                 if(x > 0 && y > 0){
25193                     scale = targetWidth / width;
25194                     
25195                     if(width < height){
25196                         scale = targetHeight / height;
25197                     }
25198                 }
25199                 
25200                 context.scale(scale, scale);
25201                 
25202                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25203                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25204
25205                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25206                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25207                 
25208                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25209                 
25210                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25211                 
25212                 break;
25213             default : 
25214                 break;
25215         }
25216         
25217         this.cropData = canvas.toDataURL(this.cropType);
25218         
25219         if(this.fireEvent('crop', this, this.cropData) !== false){
25220             this.process(this.file, this.cropData);
25221         }
25222         
25223         return;
25224         
25225     },
25226     
25227     setThumbBoxSize : function()
25228     {
25229         var width, height;
25230         
25231         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25232             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25233             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25234             
25235             this.minWidth = width;
25236             this.minHeight = height;
25237             
25238             if(this.rotate == 90 || this.rotate == 270){
25239                 this.minWidth = height;
25240                 this.minHeight = width;
25241             }
25242         }
25243         
25244         height = 300;
25245         width = Math.ceil(this.minWidth * height / this.minHeight);
25246         
25247         if(this.minWidth > this.minHeight){
25248             width = 300;
25249             height = Math.ceil(this.minHeight * width / this.minWidth);
25250         }
25251         
25252         this.thumbEl.setStyle({
25253             width : width + 'px',
25254             height : height + 'px'
25255         });
25256
25257         return;
25258             
25259     },
25260     
25261     setThumbBoxPosition : function()
25262     {
25263         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25264         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25265         
25266         this.thumbEl.setLeft(x);
25267         this.thumbEl.setTop(y);
25268         
25269     },
25270     
25271     baseRotateLevel : function()
25272     {
25273         this.baseRotate = 1;
25274         
25275         if(
25276                 typeof(this.exif) != 'undefined' &&
25277                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25278                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25279         ){
25280             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25281         }
25282         
25283         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25284         
25285     },
25286     
25287     baseScaleLevel : function()
25288     {
25289         var width, height;
25290         
25291         if(this.isDocument){
25292             
25293             if(this.baseRotate == 6 || this.baseRotate == 8){
25294             
25295                 height = this.thumbEl.getHeight();
25296                 this.baseScale = height / this.imageEl.OriginWidth;
25297
25298                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25299                     width = this.thumbEl.getWidth();
25300                     this.baseScale = width / this.imageEl.OriginHeight;
25301                 }
25302
25303                 return;
25304             }
25305
25306             height = this.thumbEl.getHeight();
25307             this.baseScale = height / this.imageEl.OriginHeight;
25308
25309             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25310                 width = this.thumbEl.getWidth();
25311                 this.baseScale = width / this.imageEl.OriginWidth;
25312             }
25313
25314             return;
25315         }
25316         
25317         if(this.baseRotate == 6 || this.baseRotate == 8){
25318             
25319             width = this.thumbEl.getHeight();
25320             this.baseScale = width / this.imageEl.OriginHeight;
25321             
25322             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25323                 height = this.thumbEl.getWidth();
25324                 this.baseScale = height / this.imageEl.OriginHeight;
25325             }
25326             
25327             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25328                 height = this.thumbEl.getWidth();
25329                 this.baseScale = height / this.imageEl.OriginHeight;
25330                 
25331                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25332                     width = this.thumbEl.getHeight();
25333                     this.baseScale = width / this.imageEl.OriginWidth;
25334                 }
25335             }
25336             
25337             return;
25338         }
25339         
25340         width = this.thumbEl.getWidth();
25341         this.baseScale = width / this.imageEl.OriginWidth;
25342         
25343         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25344             height = this.thumbEl.getHeight();
25345             this.baseScale = height / this.imageEl.OriginHeight;
25346         }
25347         
25348         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25349             
25350             height = this.thumbEl.getHeight();
25351             this.baseScale = height / this.imageEl.OriginHeight;
25352             
25353             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25354                 width = this.thumbEl.getWidth();
25355                 this.baseScale = width / this.imageEl.OriginWidth;
25356             }
25357             
25358         }
25359         
25360         return;
25361     },
25362     
25363     getScaleLevel : function()
25364     {
25365         return this.baseScale * Math.pow(1.1, this.scale);
25366     },
25367     
25368     onTouchStart : function(e)
25369     {
25370         if(!this.canvasLoaded){
25371             this.beforeSelectFile(e);
25372             return;
25373         }
25374         
25375         var touches = e.browserEvent.touches;
25376         
25377         if(!touches){
25378             return;
25379         }
25380         
25381         if(touches.length == 1){
25382             this.onMouseDown(e);
25383             return;
25384         }
25385         
25386         if(touches.length != 2){
25387             return;
25388         }
25389         
25390         var coords = [];
25391         
25392         for(var i = 0, finger; finger = touches[i]; i++){
25393             coords.push(finger.pageX, finger.pageY);
25394         }
25395         
25396         var x = Math.pow(coords[0] - coords[2], 2);
25397         var y = Math.pow(coords[1] - coords[3], 2);
25398         
25399         this.startDistance = Math.sqrt(x + y);
25400         
25401         this.startScale = this.scale;
25402         
25403         this.pinching = true;
25404         this.dragable = false;
25405         
25406     },
25407     
25408     onTouchMove : function(e)
25409     {
25410         if(!this.pinching && !this.dragable){
25411             return;
25412         }
25413         
25414         var touches = e.browserEvent.touches;
25415         
25416         if(!touches){
25417             return;
25418         }
25419         
25420         if(this.dragable){
25421             this.onMouseMove(e);
25422             return;
25423         }
25424         
25425         var coords = [];
25426         
25427         for(var i = 0, finger; finger = touches[i]; i++){
25428             coords.push(finger.pageX, finger.pageY);
25429         }
25430         
25431         var x = Math.pow(coords[0] - coords[2], 2);
25432         var y = Math.pow(coords[1] - coords[3], 2);
25433         
25434         this.endDistance = Math.sqrt(x + y);
25435         
25436         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25437         
25438         if(!this.zoomable()){
25439             this.scale = this.startScale;
25440             return;
25441         }
25442         
25443         this.draw();
25444         
25445     },
25446     
25447     onTouchEnd : function(e)
25448     {
25449         this.pinching = false;
25450         this.dragable = false;
25451         
25452     },
25453     
25454     process : function(file, crop)
25455     {
25456         if(this.loadMask){
25457             this.maskEl.mask(this.loadingText);
25458         }
25459         
25460         this.xhr = new XMLHttpRequest();
25461         
25462         file.xhr = this.xhr;
25463
25464         this.xhr.open(this.method, this.url, true);
25465         
25466         var headers = {
25467             "Accept": "application/json",
25468             "Cache-Control": "no-cache",
25469             "X-Requested-With": "XMLHttpRequest"
25470         };
25471         
25472         for (var headerName in headers) {
25473             var headerValue = headers[headerName];
25474             if (headerValue) {
25475                 this.xhr.setRequestHeader(headerName, headerValue);
25476             }
25477         }
25478         
25479         var _this = this;
25480         
25481         this.xhr.onload = function()
25482         {
25483             _this.xhrOnLoad(_this.xhr);
25484         }
25485         
25486         this.xhr.onerror = function()
25487         {
25488             _this.xhrOnError(_this.xhr);
25489         }
25490         
25491         var formData = new FormData();
25492
25493         formData.append('returnHTML', 'NO');
25494         
25495         if(crop){
25496             formData.append('crop', crop);
25497         }
25498         
25499         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25500             formData.append(this.paramName, file, file.name);
25501         }
25502         
25503         if(typeof(file.filename) != 'undefined'){
25504             formData.append('filename', file.filename);
25505         }
25506         
25507         if(typeof(file.mimetype) != 'undefined'){
25508             formData.append('mimetype', file.mimetype);
25509         }
25510         
25511         if(this.fireEvent('arrange', this, formData) != false){
25512             this.xhr.send(formData);
25513         };
25514     },
25515     
25516     xhrOnLoad : function(xhr)
25517     {
25518         if(this.loadMask){
25519             this.maskEl.unmask();
25520         }
25521         
25522         if (xhr.readyState !== 4) {
25523             this.fireEvent('exception', this, xhr);
25524             return;
25525         }
25526
25527         var response = Roo.decode(xhr.responseText);
25528         
25529         if(!response.success){
25530             this.fireEvent('exception', this, xhr);
25531             return;
25532         }
25533         
25534         var response = Roo.decode(xhr.responseText);
25535         
25536         this.fireEvent('upload', this, response);
25537         
25538     },
25539     
25540     xhrOnError : function()
25541     {
25542         if(this.loadMask){
25543             this.maskEl.unmask();
25544         }
25545         
25546         Roo.log('xhr on error');
25547         
25548         var response = Roo.decode(xhr.responseText);
25549           
25550         Roo.log(response);
25551         
25552     },
25553     
25554     prepare : function(file)
25555     {   
25556         if(this.loadMask){
25557             this.maskEl.mask(this.loadingText);
25558         }
25559         
25560         this.file = false;
25561         this.exif = {};
25562         
25563         if(typeof(file) === 'string'){
25564             this.loadCanvas(file);
25565             return;
25566         }
25567         
25568         if(!file || !this.urlAPI){
25569             return;
25570         }
25571         
25572         this.file = file;
25573         this.cropType = file.type;
25574         
25575         var _this = this;
25576         
25577         if(this.fireEvent('prepare', this, this.file) != false){
25578             
25579             var reader = new FileReader();
25580             
25581             reader.onload = function (e) {
25582                 if (e.target.error) {
25583                     Roo.log(e.target.error);
25584                     return;
25585                 }
25586                 
25587                 var buffer = e.target.result,
25588                     dataView = new DataView(buffer),
25589                     offset = 2,
25590                     maxOffset = dataView.byteLength - 4,
25591                     markerBytes,
25592                     markerLength;
25593                 
25594                 if (dataView.getUint16(0) === 0xffd8) {
25595                     while (offset < maxOffset) {
25596                         markerBytes = dataView.getUint16(offset);
25597                         
25598                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25599                             markerLength = dataView.getUint16(offset + 2) + 2;
25600                             if (offset + markerLength > dataView.byteLength) {
25601                                 Roo.log('Invalid meta data: Invalid segment size.');
25602                                 break;
25603                             }
25604                             
25605                             if(markerBytes == 0xffe1){
25606                                 _this.parseExifData(
25607                                     dataView,
25608                                     offset,
25609                                     markerLength
25610                                 );
25611                             }
25612                             
25613                             offset += markerLength;
25614                             
25615                             continue;
25616                         }
25617                         
25618                         break;
25619                     }
25620                     
25621                 }
25622                 
25623                 var url = _this.urlAPI.createObjectURL(_this.file);
25624                 
25625                 _this.loadCanvas(url);
25626                 
25627                 return;
25628             }
25629             
25630             reader.readAsArrayBuffer(this.file);
25631             
25632         }
25633         
25634     },
25635     
25636     parseExifData : function(dataView, offset, length)
25637     {
25638         var tiffOffset = offset + 10,
25639             littleEndian,
25640             dirOffset;
25641     
25642         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25643             // No Exif data, might be XMP data instead
25644             return;
25645         }
25646         
25647         // Check for the ASCII code for "Exif" (0x45786966):
25648         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25649             // No Exif data, might be XMP data instead
25650             return;
25651         }
25652         if (tiffOffset + 8 > dataView.byteLength) {
25653             Roo.log('Invalid Exif data: Invalid segment size.');
25654             return;
25655         }
25656         // Check for the two null bytes:
25657         if (dataView.getUint16(offset + 8) !== 0x0000) {
25658             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25659             return;
25660         }
25661         // Check the byte alignment:
25662         switch (dataView.getUint16(tiffOffset)) {
25663         case 0x4949:
25664             littleEndian = true;
25665             break;
25666         case 0x4D4D:
25667             littleEndian = false;
25668             break;
25669         default:
25670             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25671             return;
25672         }
25673         // Check for the TIFF tag marker (0x002A):
25674         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25675             Roo.log('Invalid Exif data: Missing TIFF marker.');
25676             return;
25677         }
25678         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25679         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25680         
25681         this.parseExifTags(
25682             dataView,
25683             tiffOffset,
25684             tiffOffset + dirOffset,
25685             littleEndian
25686         );
25687     },
25688     
25689     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25690     {
25691         var tagsNumber,
25692             dirEndOffset,
25693             i;
25694         if (dirOffset + 6 > dataView.byteLength) {
25695             Roo.log('Invalid Exif data: Invalid directory offset.');
25696             return;
25697         }
25698         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25699         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25700         if (dirEndOffset + 4 > dataView.byteLength) {
25701             Roo.log('Invalid Exif data: Invalid directory size.');
25702             return;
25703         }
25704         for (i = 0; i < tagsNumber; i += 1) {
25705             this.parseExifTag(
25706                 dataView,
25707                 tiffOffset,
25708                 dirOffset + 2 + 12 * i, // tag offset
25709                 littleEndian
25710             );
25711         }
25712         // Return the offset to the next directory:
25713         return dataView.getUint32(dirEndOffset, littleEndian);
25714     },
25715     
25716     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25717     {
25718         var tag = dataView.getUint16(offset, littleEndian);
25719         
25720         this.exif[tag] = this.getExifValue(
25721             dataView,
25722             tiffOffset,
25723             offset,
25724             dataView.getUint16(offset + 2, littleEndian), // tag type
25725             dataView.getUint32(offset + 4, littleEndian), // tag length
25726             littleEndian
25727         );
25728     },
25729     
25730     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25731     {
25732         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25733             tagSize,
25734             dataOffset,
25735             values,
25736             i,
25737             str,
25738             c;
25739     
25740         if (!tagType) {
25741             Roo.log('Invalid Exif data: Invalid tag type.');
25742             return;
25743         }
25744         
25745         tagSize = tagType.size * length;
25746         // Determine if the value is contained in the dataOffset bytes,
25747         // or if the value at the dataOffset is a pointer to the actual data:
25748         dataOffset = tagSize > 4 ?
25749                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25750         if (dataOffset + tagSize > dataView.byteLength) {
25751             Roo.log('Invalid Exif data: Invalid data offset.');
25752             return;
25753         }
25754         if (length === 1) {
25755             return tagType.getValue(dataView, dataOffset, littleEndian);
25756         }
25757         values = [];
25758         for (i = 0; i < length; i += 1) {
25759             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25760         }
25761         
25762         if (tagType.ascii) {
25763             str = '';
25764             // Concatenate the chars:
25765             for (i = 0; i < values.length; i += 1) {
25766                 c = values[i];
25767                 // Ignore the terminating NULL byte(s):
25768                 if (c === '\u0000') {
25769                     break;
25770                 }
25771                 str += c;
25772             }
25773             return str;
25774         }
25775         return values;
25776     }
25777     
25778 });
25779
25780 Roo.apply(Roo.bootstrap.UploadCropbox, {
25781     tags : {
25782         'Orientation': 0x0112
25783     },
25784     
25785     Orientation: {
25786             1: 0, //'top-left',
25787 //            2: 'top-right',
25788             3: 180, //'bottom-right',
25789 //            4: 'bottom-left',
25790 //            5: 'left-top',
25791             6: 90, //'right-top',
25792 //            7: 'right-bottom',
25793             8: 270 //'left-bottom'
25794     },
25795     
25796     exifTagTypes : {
25797         // byte, 8-bit unsigned int:
25798         1: {
25799             getValue: function (dataView, dataOffset) {
25800                 return dataView.getUint8(dataOffset);
25801             },
25802             size: 1
25803         },
25804         // ascii, 8-bit byte:
25805         2: {
25806             getValue: function (dataView, dataOffset) {
25807                 return String.fromCharCode(dataView.getUint8(dataOffset));
25808             },
25809             size: 1,
25810             ascii: true
25811         },
25812         // short, 16 bit int:
25813         3: {
25814             getValue: function (dataView, dataOffset, littleEndian) {
25815                 return dataView.getUint16(dataOffset, littleEndian);
25816             },
25817             size: 2
25818         },
25819         // long, 32 bit int:
25820         4: {
25821             getValue: function (dataView, dataOffset, littleEndian) {
25822                 return dataView.getUint32(dataOffset, littleEndian);
25823             },
25824             size: 4
25825         },
25826         // rational = two long values, first is numerator, second is denominator:
25827         5: {
25828             getValue: function (dataView, dataOffset, littleEndian) {
25829                 return dataView.getUint32(dataOffset, littleEndian) /
25830                     dataView.getUint32(dataOffset + 4, littleEndian);
25831             },
25832             size: 8
25833         },
25834         // slong, 32 bit signed int:
25835         9: {
25836             getValue: function (dataView, dataOffset, littleEndian) {
25837                 return dataView.getInt32(dataOffset, littleEndian);
25838             },
25839             size: 4
25840         },
25841         // srational, two slongs, first is numerator, second is denominator:
25842         10: {
25843             getValue: function (dataView, dataOffset, littleEndian) {
25844                 return dataView.getInt32(dataOffset, littleEndian) /
25845                     dataView.getInt32(dataOffset + 4, littleEndian);
25846             },
25847             size: 8
25848         }
25849     },
25850     
25851     footer : {
25852         STANDARD : [
25853             {
25854                 tag : 'div',
25855                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25856                 action : 'rotate-left',
25857                 cn : [
25858                     {
25859                         tag : 'button',
25860                         cls : 'btn btn-default',
25861                         html : '<i class="fa fa-undo"></i>'
25862                     }
25863                 ]
25864             },
25865             {
25866                 tag : 'div',
25867                 cls : 'btn-group roo-upload-cropbox-picture',
25868                 action : 'picture',
25869                 cn : [
25870                     {
25871                         tag : 'button',
25872                         cls : 'btn btn-default',
25873                         html : '<i class="fa fa-picture-o"></i>'
25874                     }
25875                 ]
25876             },
25877             {
25878                 tag : 'div',
25879                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25880                 action : 'rotate-right',
25881                 cn : [
25882                     {
25883                         tag : 'button',
25884                         cls : 'btn btn-default',
25885                         html : '<i class="fa fa-repeat"></i>'
25886                     }
25887                 ]
25888             }
25889         ],
25890         DOCUMENT : [
25891             {
25892                 tag : 'div',
25893                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25894                 action : 'rotate-left',
25895                 cn : [
25896                     {
25897                         tag : 'button',
25898                         cls : 'btn btn-default',
25899                         html : '<i class="fa fa-undo"></i>'
25900                     }
25901                 ]
25902             },
25903             {
25904                 tag : 'div',
25905                 cls : 'btn-group roo-upload-cropbox-download',
25906                 action : 'download',
25907                 cn : [
25908                     {
25909                         tag : 'button',
25910                         cls : 'btn btn-default',
25911                         html : '<i class="fa fa-download"></i>'
25912                     }
25913                 ]
25914             },
25915             {
25916                 tag : 'div',
25917                 cls : 'btn-group roo-upload-cropbox-crop',
25918                 action : 'crop',
25919                 cn : [
25920                     {
25921                         tag : 'button',
25922                         cls : 'btn btn-default',
25923                         html : '<i class="fa fa-crop"></i>'
25924                     }
25925                 ]
25926             },
25927             {
25928                 tag : 'div',
25929                 cls : 'btn-group roo-upload-cropbox-trash',
25930                 action : 'trash',
25931                 cn : [
25932                     {
25933                         tag : 'button',
25934                         cls : 'btn btn-default',
25935                         html : '<i class="fa fa-trash"></i>'
25936                     }
25937                 ]
25938             },
25939             {
25940                 tag : 'div',
25941                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25942                 action : 'rotate-right',
25943                 cn : [
25944                     {
25945                         tag : 'button',
25946                         cls : 'btn btn-default',
25947                         html : '<i class="fa fa-repeat"></i>'
25948                     }
25949                 ]
25950             }
25951         ],
25952         ROTATOR : [
25953             {
25954                 tag : 'div',
25955                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25956                 action : 'rotate-left',
25957                 cn : [
25958                     {
25959                         tag : 'button',
25960                         cls : 'btn btn-default',
25961                         html : '<i class="fa fa-undo"></i>'
25962                     }
25963                 ]
25964             },
25965             {
25966                 tag : 'div',
25967                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25968                 action : 'rotate-right',
25969                 cn : [
25970                     {
25971                         tag : 'button',
25972                         cls : 'btn btn-default',
25973                         html : '<i class="fa fa-repeat"></i>'
25974                     }
25975                 ]
25976             }
25977         ]
25978     }
25979 });
25980
25981 /*
25982 * Licence: LGPL
25983 */
25984
25985 /**
25986  * @class Roo.bootstrap.DocumentManager
25987  * @extends Roo.bootstrap.Component
25988  * Bootstrap DocumentManager class
25989  * @cfg {String} paramName default 'imageUpload'
25990  * @cfg {String} method default POST
25991  * @cfg {String} url action url
25992  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25993  * @cfg {Boolean} multiple multiple upload default true
25994  * @cfg {Number} thumbSize default 300
25995  * @cfg {String} fieldLabel
25996  * @cfg {Number} labelWidth default 4
25997  * @cfg {String} labelAlign (left|top) default left
25998  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25999  * 
26000  * @constructor
26001  * Create a new DocumentManager
26002  * @param {Object} config The config object
26003  */
26004
26005 Roo.bootstrap.DocumentManager = function(config){
26006     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26007     
26008     this.addEvents({
26009         /**
26010          * @event initial
26011          * Fire when initial the DocumentManager
26012          * @param {Roo.bootstrap.DocumentManager} this
26013          */
26014         "initial" : true,
26015         /**
26016          * @event inspect
26017          * inspect selected file
26018          * @param {Roo.bootstrap.DocumentManager} this
26019          * @param {File} file
26020          */
26021         "inspect" : true,
26022         /**
26023          * @event exception
26024          * Fire when xhr load exception
26025          * @param {Roo.bootstrap.DocumentManager} this
26026          * @param {XMLHttpRequest} xhr
26027          */
26028         "exception" : true,
26029         /**
26030          * @event prepare
26031          * prepare the form data
26032          * @param {Roo.bootstrap.DocumentManager} this
26033          * @param {Object} formData
26034          */
26035         "prepare" : true,
26036         /**
26037          * @event remove
26038          * Fire when remove the file
26039          * @param {Roo.bootstrap.DocumentManager} this
26040          * @param {Object} file
26041          */
26042         "remove" : true,
26043         /**
26044          * @event refresh
26045          * Fire after refresh the file
26046          * @param {Roo.bootstrap.DocumentManager} this
26047          */
26048         "refresh" : true,
26049         /**
26050          * @event click
26051          * Fire after click the image
26052          * @param {Roo.bootstrap.DocumentManager} this
26053          * @param {Object} file
26054          */
26055         "click" : true,
26056         /**
26057          * @event edit
26058          * Fire when upload a image and editable set to true
26059          * @param {Roo.bootstrap.DocumentManager} this
26060          * @param {Object} file
26061          */
26062         "edit" : true,
26063         /**
26064          * @event beforeselectfile
26065          * Fire before select file
26066          * @param {Roo.bootstrap.DocumentManager} this
26067          */
26068         "beforeselectfile" : true,
26069         /**
26070          * @event process
26071          * Fire before process file
26072          * @param {Roo.bootstrap.DocumentManager} this
26073          * @param {Object} file
26074          */
26075         "process" : true
26076         
26077     });
26078 };
26079
26080 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26081     
26082     boxes : 0,
26083     inputName : '',
26084     thumbSize : 300,
26085     multiple : true,
26086     files : [],
26087     method : 'POST',
26088     url : '',
26089     paramName : 'imageUpload',
26090     fieldLabel : '',
26091     labelWidth : 4,
26092     labelAlign : 'left',
26093     editable : true,
26094     delegates : [],
26095     
26096     
26097     xhr : false, 
26098     
26099     getAutoCreate : function()
26100     {   
26101         var managerWidget = {
26102             tag : 'div',
26103             cls : 'roo-document-manager',
26104             cn : [
26105                 {
26106                     tag : 'input',
26107                     cls : 'roo-document-manager-selector',
26108                     type : 'file'
26109                 },
26110                 {
26111                     tag : 'div',
26112                     cls : 'roo-document-manager-uploader',
26113                     cn : [
26114                         {
26115                             tag : 'div',
26116                             cls : 'roo-document-manager-upload-btn',
26117                             html : '<i class="fa fa-plus"></i>'
26118                         }
26119                     ]
26120                     
26121                 }
26122             ]
26123         };
26124         
26125         var content = [
26126             {
26127                 tag : 'div',
26128                 cls : 'column col-md-12',
26129                 cn : managerWidget
26130             }
26131         ];
26132         
26133         if(this.fieldLabel.length){
26134             
26135             content = [
26136                 {
26137                     tag : 'div',
26138                     cls : 'column col-md-12',
26139                     html : this.fieldLabel
26140                 },
26141                 {
26142                     tag : 'div',
26143                     cls : 'column col-md-12',
26144                     cn : managerWidget
26145                 }
26146             ];
26147
26148             if(this.labelAlign == 'left'){
26149                 content = [
26150                     {
26151                         tag : 'div',
26152                         cls : 'column col-md-' + this.labelWidth,
26153                         html : this.fieldLabel
26154                     },
26155                     {
26156                         tag : 'div',
26157                         cls : 'column col-md-' + (12 - this.labelWidth),
26158                         cn : managerWidget
26159                     }
26160                 ];
26161                 
26162             }
26163         }
26164         
26165         var cfg = {
26166             tag : 'div',
26167             cls : 'row clearfix',
26168             cn : content
26169         };
26170         
26171         return cfg;
26172         
26173     },
26174     
26175     initEvents : function()
26176     {
26177         this.managerEl = this.el.select('.roo-document-manager', true).first();
26178         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26179         
26180         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26181         this.selectorEl.hide();
26182         
26183         if(this.multiple){
26184             this.selectorEl.attr('multiple', 'multiple');
26185         }
26186         
26187         this.selectorEl.on('change', this.onFileSelected, this);
26188         
26189         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26190         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26191         
26192         this.uploader.on('click', this.onUploaderClick, this);
26193         
26194         this.renderProgressDialog();
26195         
26196         var _this = this;
26197         
26198         window.addEventListener("resize", function() { _this.refresh(); } );
26199         
26200         this.fireEvent('initial', this);
26201     },
26202     
26203     renderProgressDialog : function()
26204     {
26205         var _this = this;
26206         
26207         this.progressDialog = new Roo.bootstrap.Modal({
26208             cls : 'roo-document-manager-progress-dialog',
26209             allow_close : false,
26210             title : '',
26211             buttons : [
26212                 {
26213                     name  :'cancel',
26214                     weight : 'danger',
26215                     html : 'Cancel'
26216                 }
26217             ], 
26218             listeners : { 
26219                 btnclick : function() {
26220                     _this.uploadCancel();
26221                     this.hide();
26222                 }
26223             }
26224         });
26225          
26226         this.progressDialog.render(Roo.get(document.body));
26227          
26228         this.progress = new Roo.bootstrap.Progress({
26229             cls : 'roo-document-manager-progress',
26230             active : true,
26231             striped : true
26232         });
26233         
26234         this.progress.render(this.progressDialog.getChildContainer());
26235         
26236         this.progressBar = new Roo.bootstrap.ProgressBar({
26237             cls : 'roo-document-manager-progress-bar',
26238             aria_valuenow : 0,
26239             aria_valuemin : 0,
26240             aria_valuemax : 12,
26241             panel : 'success'
26242         });
26243         
26244         this.progressBar.render(this.progress.getChildContainer());
26245     },
26246     
26247     onUploaderClick : function(e)
26248     {
26249         e.preventDefault();
26250      
26251         if(this.fireEvent('beforeselectfile', this) != false){
26252             this.selectorEl.dom.click();
26253         }
26254         
26255     },
26256     
26257     onFileSelected : function(e)
26258     {
26259         e.preventDefault();
26260         
26261         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26262             return;
26263         }
26264         
26265         Roo.each(this.selectorEl.dom.files, function(file){
26266             if(this.fireEvent('inspect', this, file) != false){
26267                 this.files.push(file);
26268             }
26269         }, this);
26270         
26271         this.queue();
26272         
26273     },
26274     
26275     queue : function()
26276     {
26277         this.selectorEl.dom.value = '';
26278         
26279         if(!this.files.length){
26280             return;
26281         }
26282         
26283         if(this.boxes > 0 && this.files.length > this.boxes){
26284             this.files = this.files.slice(0, this.boxes);
26285         }
26286         
26287         this.uploader.show();
26288         
26289         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26290             this.uploader.hide();
26291         }
26292         
26293         var _this = this;
26294         
26295         var files = [];
26296         
26297         var docs = [];
26298         
26299         Roo.each(this.files, function(file){
26300             
26301             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26302                 var f = this.renderPreview(file);
26303                 files.push(f);
26304                 return;
26305             }
26306             
26307             if(file.type.indexOf('image') != -1){
26308                 this.delegates.push(
26309                     (function(){
26310                         _this.process(file);
26311                     }).createDelegate(this)
26312                 );
26313         
26314                 return;
26315             }
26316             
26317             docs.push(
26318                 (function(){
26319                     _this.process(file);
26320                 }).createDelegate(this)
26321             );
26322             
26323         }, this);
26324         
26325         this.files = files;
26326         
26327         this.delegates = this.delegates.concat(docs);
26328         
26329         if(!this.delegates.length){
26330             this.refresh();
26331             return;
26332         }
26333         
26334         this.progressBar.aria_valuemax = this.delegates.length;
26335         
26336         this.arrange();
26337         
26338         return;
26339     },
26340     
26341     arrange : function()
26342     {
26343         if(!this.delegates.length){
26344             this.progressDialog.hide();
26345             this.refresh();
26346             return;
26347         }
26348         
26349         var delegate = this.delegates.shift();
26350         
26351         this.progressDialog.show();
26352         
26353         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26354         
26355         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26356         
26357         delegate();
26358     },
26359     
26360     refresh : function()
26361     {
26362         this.uploader.show();
26363         
26364         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26365             this.uploader.hide();
26366         }
26367         
26368         Roo.isTouch ? this.closable(false) : this.closable(true);
26369         
26370         this.fireEvent('refresh', this);
26371     },
26372     
26373     onRemove : function(e, el, o)
26374     {
26375         e.preventDefault();
26376         
26377         this.fireEvent('remove', this, o);
26378         
26379     },
26380     
26381     remove : function(o)
26382     {
26383         var files = [];
26384         
26385         Roo.each(this.files, function(file){
26386             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26387                 files.push(file);
26388                 return;
26389             }
26390
26391             o.target.remove();
26392
26393         }, this);
26394         
26395         this.files = files;
26396         
26397         this.refresh();
26398     },
26399     
26400     clear : function()
26401     {
26402         Roo.each(this.files, function(file){
26403             if(!file.target){
26404                 return;
26405             }
26406             
26407             file.target.remove();
26408
26409         }, this);
26410         
26411         this.files = [];
26412         
26413         this.refresh();
26414     },
26415     
26416     onClick : function(e, el, o)
26417     {
26418         e.preventDefault();
26419         
26420         this.fireEvent('click', this, o);
26421         
26422     },
26423     
26424     closable : function(closable)
26425     {
26426         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26427             
26428             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26429             
26430             if(closable){
26431                 el.show();
26432                 return;
26433             }
26434             
26435             el.hide();
26436             
26437         }, this);
26438     },
26439     
26440     xhrOnLoad : function(xhr)
26441     {
26442         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26443             el.remove();
26444         }, this);
26445         
26446         if (xhr.readyState !== 4) {
26447             this.arrange();
26448             this.fireEvent('exception', this, xhr);
26449             return;
26450         }
26451
26452         var response = Roo.decode(xhr.responseText);
26453         
26454         if(!response.success){
26455             this.arrange();
26456             this.fireEvent('exception', this, xhr);
26457             return;
26458         }
26459         
26460         var file = this.renderPreview(response.data);
26461         
26462         this.files.push(file);
26463         
26464         this.arrange();
26465         
26466     },
26467     
26468     xhrOnError : function()
26469     {
26470         Roo.log('xhr on error');
26471         
26472         var response = Roo.decode(xhr.responseText);
26473           
26474         Roo.log(response);
26475         
26476         this.arrange();
26477     },
26478     
26479     process : function(file)
26480     {
26481         if(this.fireEvent('process', this, file) !== false){
26482             if(this.editable && file.type.indexOf('image') != -1){
26483                 this.fireEvent('edit', this, file);
26484                 return;
26485             }
26486
26487             this.uploadStart(file, false);
26488
26489             return;
26490         }
26491         
26492     },
26493     
26494     uploadStart : function(file, crop)
26495     {
26496         this.xhr = new XMLHttpRequest();
26497         
26498         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26499             this.arrange();
26500             return;
26501         }
26502         
26503         file.xhr = this.xhr;
26504             
26505         this.managerEl.createChild({
26506             tag : 'div',
26507             cls : 'roo-document-manager-loading',
26508             cn : [
26509                 {
26510                     tag : 'div',
26511                     tooltip : file.name,
26512                     cls : 'roo-document-manager-thumb',
26513                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26514                 }
26515             ]
26516
26517         });
26518
26519         this.xhr.open(this.method, this.url, true);
26520         
26521         var headers = {
26522             "Accept": "application/json",
26523             "Cache-Control": "no-cache",
26524             "X-Requested-With": "XMLHttpRequest"
26525         };
26526         
26527         for (var headerName in headers) {
26528             var headerValue = headers[headerName];
26529             if (headerValue) {
26530                 this.xhr.setRequestHeader(headerName, headerValue);
26531             }
26532         }
26533         
26534         var _this = this;
26535         
26536         this.xhr.onload = function()
26537         {
26538             _this.xhrOnLoad(_this.xhr);
26539         }
26540         
26541         this.xhr.onerror = function()
26542         {
26543             _this.xhrOnError(_this.xhr);
26544         }
26545         
26546         var formData = new FormData();
26547
26548         formData.append('returnHTML', 'NO');
26549         
26550         if(crop){
26551             formData.append('crop', crop);
26552         }
26553         
26554         formData.append(this.paramName, file, file.name);
26555         
26556         if(this.fireEvent('prepare', this, formData) != false){
26557             this.xhr.send(formData);
26558         };
26559     },
26560     
26561     uploadCancel : function()
26562     {
26563         if (this.xhr) {
26564             this.xhr.abort();
26565         }
26566         
26567         
26568         this.delegates = [];
26569         
26570         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26571             el.remove();
26572         }, this);
26573         
26574         this.arrange();
26575     },
26576     
26577     renderPreview : function(file)
26578     {
26579         if(typeof(file.target) != 'undefined' && file.target){
26580             return file;
26581         }
26582         
26583         var previewEl = this.managerEl.createChild({
26584             tag : 'div',
26585             cls : 'roo-document-manager-preview',
26586             cn : [
26587                 {
26588                     tag : 'div',
26589                     tooltip : file.filename,
26590                     cls : 'roo-document-manager-thumb',
26591                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26592                 },
26593                 {
26594                     tag : 'button',
26595                     cls : 'close',
26596                     html : '<i class="fa fa-times-circle"></i>'
26597                 }
26598             ]
26599         });
26600
26601         var close = previewEl.select('button.close', true).first();
26602
26603         close.on('click', this.onRemove, this, file);
26604
26605         file.target = previewEl;
26606
26607         var image = previewEl.select('img', true).first();
26608         
26609         var _this = this;
26610         
26611         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26612         
26613         image.on('click', this.onClick, this, file);
26614         
26615         return file;
26616         
26617     },
26618     
26619     onPreviewLoad : function(file, image)
26620     {
26621         if(typeof(file.target) == 'undefined' || !file.target){
26622             return;
26623         }
26624         
26625         var width = image.dom.naturalWidth || image.dom.width;
26626         var height = image.dom.naturalHeight || image.dom.height;
26627         
26628         if(width > height){
26629             file.target.addClass('wide');
26630             return;
26631         }
26632         
26633         file.target.addClass('tall');
26634         return;
26635         
26636     },
26637     
26638     uploadFromSource : function(file, crop)
26639     {
26640         this.xhr = new XMLHttpRequest();
26641         
26642         this.managerEl.createChild({
26643             tag : 'div',
26644             cls : 'roo-document-manager-loading',
26645             cn : [
26646                 {
26647                     tag : 'div',
26648                     tooltip : file.name,
26649                     cls : 'roo-document-manager-thumb',
26650                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26651                 }
26652             ]
26653
26654         });
26655
26656         this.xhr.open(this.method, this.url, true);
26657         
26658         var headers = {
26659             "Accept": "application/json",
26660             "Cache-Control": "no-cache",
26661             "X-Requested-With": "XMLHttpRequest"
26662         };
26663         
26664         for (var headerName in headers) {
26665             var headerValue = headers[headerName];
26666             if (headerValue) {
26667                 this.xhr.setRequestHeader(headerName, headerValue);
26668             }
26669         }
26670         
26671         var _this = this;
26672         
26673         this.xhr.onload = function()
26674         {
26675             _this.xhrOnLoad(_this.xhr);
26676         }
26677         
26678         this.xhr.onerror = function()
26679         {
26680             _this.xhrOnError(_this.xhr);
26681         }
26682         
26683         var formData = new FormData();
26684
26685         formData.append('returnHTML', 'NO');
26686         
26687         formData.append('crop', crop);
26688         
26689         if(typeof(file.filename) != 'undefined'){
26690             formData.append('filename', file.filename);
26691         }
26692         
26693         if(typeof(file.mimetype) != 'undefined'){
26694             formData.append('mimetype', file.mimetype);
26695         }
26696         
26697         if(this.fireEvent('prepare', this, formData) != false){
26698             this.xhr.send(formData);
26699         };
26700     }
26701 });
26702
26703 /*
26704 * Licence: LGPL
26705 */
26706
26707 /**
26708  * @class Roo.bootstrap.DocumentViewer
26709  * @extends Roo.bootstrap.Component
26710  * Bootstrap DocumentViewer class
26711  * 
26712  * @constructor
26713  * Create a new DocumentViewer
26714  * @param {Object} config The config object
26715  */
26716
26717 Roo.bootstrap.DocumentViewer = function(config){
26718     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26719     
26720     this.addEvents({
26721         /**
26722          * @event initial
26723          * Fire after initEvent
26724          * @param {Roo.bootstrap.DocumentViewer} this
26725          */
26726         "initial" : true,
26727         /**
26728          * @event click
26729          * Fire after click
26730          * @param {Roo.bootstrap.DocumentViewer} this
26731          */
26732         "click" : true,
26733         /**
26734          * @event trash
26735          * Fire after trash button
26736          * @param {Roo.bootstrap.DocumentViewer} this
26737          */
26738         "trash" : true
26739         
26740     });
26741 };
26742
26743 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26744     
26745     getAutoCreate : function()
26746     {
26747         var cfg = {
26748             tag : 'div',
26749             cls : 'roo-document-viewer',
26750             cn : [
26751                 {
26752                     tag : 'div',
26753                     cls : 'roo-document-viewer-body',
26754                     cn : [
26755                         {
26756                             tag : 'div',
26757                             cls : 'roo-document-viewer-thumb',
26758                             cn : [
26759                                 {
26760                                     tag : 'img',
26761                                     cls : 'roo-document-viewer-image'
26762                                 }
26763                             ]
26764                         }
26765                     ]
26766                 },
26767                 {
26768                     tag : 'div',
26769                     cls : 'roo-document-viewer-footer',
26770                     cn : {
26771                         tag : 'div',
26772                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26773                         cn : [
26774                             {
26775                                 tag : 'div',
26776                                 cls : 'btn-group',
26777                                 cn : [
26778                                     {
26779                                         tag : 'button',
26780                                         cls : 'btn btn-default roo-document-viewer-trash',
26781                                         html : '<i class="fa fa-trash"></i>'
26782                                     }
26783                                 ]
26784                             }
26785                         ]
26786                     }
26787                 }
26788             ]
26789         };
26790         
26791         return cfg;
26792     },
26793     
26794     initEvents : function()
26795     {
26796         
26797         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26798         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26799         
26800         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26801         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26802         
26803         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26804         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26805         
26806         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26807         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26808         
26809         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26810         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26811         
26812         this.bodyEl.on('click', this.onClick, this);
26813         
26814         this.trashBtn.on('click', this.onTrash, this);
26815         
26816     },
26817     
26818     initial : function()
26819     {
26820 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26821         
26822         
26823         this.fireEvent('initial', this);
26824         
26825     },
26826     
26827     onClick : function(e)
26828     {
26829         e.preventDefault();
26830         
26831         this.fireEvent('click', this);
26832     },
26833     
26834     onTrash : function(e)
26835     {
26836         e.preventDefault();
26837         
26838         this.fireEvent('trash', this);
26839     }
26840     
26841 });
26842 /*
26843  * - LGPL
26844  *
26845  * nav progress bar
26846  * 
26847  */
26848
26849 /**
26850  * @class Roo.bootstrap.NavProgressBar
26851  * @extends Roo.bootstrap.Component
26852  * Bootstrap NavProgressBar class
26853  * 
26854  * @constructor
26855  * Create a new nav progress bar
26856  * @param {Object} config The config object
26857  */
26858
26859 Roo.bootstrap.NavProgressBar = function(config){
26860     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26861
26862     this.bullets = this.bullets || [];
26863    
26864 //    Roo.bootstrap.NavProgressBar.register(this);
26865      this.addEvents({
26866         /**
26867              * @event changed
26868              * Fires when the active item changes
26869              * @param {Roo.bootstrap.NavProgressBar} this
26870              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26871              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26872          */
26873         'changed': true
26874      });
26875     
26876 };
26877
26878 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26879     
26880     bullets : [],
26881     barItems : [],
26882     
26883     getAutoCreate : function()
26884     {
26885         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26886         
26887         cfg = {
26888             tag : 'div',
26889             cls : 'roo-navigation-bar-group',
26890             cn : [
26891                 {
26892                     tag : 'div',
26893                     cls : 'roo-navigation-top-bar'
26894                 },
26895                 {
26896                     tag : 'div',
26897                     cls : 'roo-navigation-bullets-bar',
26898                     cn : [
26899                         {
26900                             tag : 'ul',
26901                             cls : 'roo-navigation-bar'
26902                         }
26903                     ]
26904                 },
26905                 
26906                 {
26907                     tag : 'div',
26908                     cls : 'roo-navigation-bottom-bar'
26909                 }
26910             ]
26911             
26912         };
26913         
26914         return cfg;
26915         
26916     },
26917     
26918     initEvents: function() 
26919     {
26920         
26921     },
26922     
26923     onRender : function(ct, position) 
26924     {
26925         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26926         
26927         if(this.bullets.length){
26928             Roo.each(this.bullets, function(b){
26929                this.addItem(b);
26930             }, this);
26931         }
26932         
26933         this.format();
26934         
26935     },
26936     
26937     addItem : function(cfg)
26938     {
26939         var item = new Roo.bootstrap.NavProgressItem(cfg);
26940         
26941         item.parentId = this.id;
26942         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26943         
26944         if(cfg.html){
26945             var top = new Roo.bootstrap.Element({
26946                 tag : 'div',
26947                 cls : 'roo-navigation-bar-text'
26948             });
26949             
26950             var bottom = new Roo.bootstrap.Element({
26951                 tag : 'div',
26952                 cls : 'roo-navigation-bar-text'
26953             });
26954             
26955             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26956             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26957             
26958             var topText = new Roo.bootstrap.Element({
26959                 tag : 'span',
26960                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26961             });
26962             
26963             var bottomText = new Roo.bootstrap.Element({
26964                 tag : 'span',
26965                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26966             });
26967             
26968             topText.onRender(top.el, null);
26969             bottomText.onRender(bottom.el, null);
26970             
26971             item.topEl = top;
26972             item.bottomEl = bottom;
26973         }
26974         
26975         this.barItems.push(item);
26976         
26977         return item;
26978     },
26979     
26980     getActive : function()
26981     {
26982         var active = false;
26983         
26984         Roo.each(this.barItems, function(v){
26985             
26986             if (!v.isActive()) {
26987                 return;
26988             }
26989             
26990             active = v;
26991             return false;
26992             
26993         });
26994         
26995         return active;
26996     },
26997     
26998     setActiveItem : function(item)
26999     {
27000         var prev = false;
27001         
27002         Roo.each(this.barItems, function(v){
27003             if (v.rid == item.rid) {
27004                 return ;
27005             }
27006             
27007             if (v.isActive()) {
27008                 v.setActive(false);
27009                 prev = v;
27010             }
27011         });
27012
27013         item.setActive(true);
27014         
27015         this.fireEvent('changed', this, item, prev);
27016     },
27017     
27018     getBarItem: function(rid)
27019     {
27020         var ret = false;
27021         
27022         Roo.each(this.barItems, function(e) {
27023             if (e.rid != rid) {
27024                 return;
27025             }
27026             
27027             ret =  e;
27028             return false;
27029         });
27030         
27031         return ret;
27032     },
27033     
27034     indexOfItem : function(item)
27035     {
27036         var index = false;
27037         
27038         Roo.each(this.barItems, function(v, i){
27039             
27040             if (v.rid != item.rid) {
27041                 return;
27042             }
27043             
27044             index = i;
27045             return false
27046         });
27047         
27048         return index;
27049     },
27050     
27051     setActiveNext : function()
27052     {
27053         var i = this.indexOfItem(this.getActive());
27054         
27055         if (i > this.barItems.length) {
27056             return;
27057         }
27058         
27059         this.setActiveItem(this.barItems[i+1]);
27060     },
27061     
27062     setActivePrev : function()
27063     {
27064         var i = this.indexOfItem(this.getActive());
27065         
27066         if (i  < 1) {
27067             return;
27068         }
27069         
27070         this.setActiveItem(this.barItems[i-1]);
27071     },
27072     
27073     format : function()
27074     {
27075         if(!this.barItems.length){
27076             return;
27077         }
27078      
27079         var width = 100 / this.barItems.length;
27080         
27081         Roo.each(this.barItems, function(i){
27082             i.el.setStyle('width', width + '%');
27083             i.topEl.el.setStyle('width', width + '%');
27084             i.bottomEl.el.setStyle('width', width + '%');
27085         }, this);
27086         
27087     }
27088     
27089 });
27090 /*
27091  * - LGPL
27092  *
27093  * Nav Progress Item
27094  * 
27095  */
27096
27097 /**
27098  * @class Roo.bootstrap.NavProgressItem
27099  * @extends Roo.bootstrap.Component
27100  * Bootstrap NavProgressItem class
27101  * @cfg {String} rid the reference id
27102  * @cfg {Boolean} active (true|false) Is item active default false
27103  * @cfg {Boolean} disabled (true|false) Is item active default false
27104  * @cfg {String} html
27105  * @cfg {String} position (top|bottom) text position default bottom
27106  * @cfg {String} icon show icon instead of number
27107  * 
27108  * @constructor
27109  * Create a new NavProgressItem
27110  * @param {Object} config The config object
27111  */
27112 Roo.bootstrap.NavProgressItem = function(config){
27113     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27114     this.addEvents({
27115         // raw events
27116         /**
27117          * @event click
27118          * The raw click event for the entire grid.
27119          * @param {Roo.bootstrap.NavProgressItem} this
27120          * @param {Roo.EventObject} e
27121          */
27122         "click" : true
27123     });
27124    
27125 };
27126
27127 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27128     
27129     rid : '',
27130     active : false,
27131     disabled : false,
27132     html : '',
27133     position : 'bottom',
27134     icon : false,
27135     
27136     getAutoCreate : function()
27137     {
27138         var iconCls = 'roo-navigation-bar-item-icon';
27139         
27140         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27141         
27142         var cfg = {
27143             tag: 'li',
27144             cls: 'roo-navigation-bar-item',
27145             cn : [
27146                 {
27147                     tag : 'i',
27148                     cls : iconCls
27149                 }
27150             ]
27151         };
27152         
27153         if(this.active){
27154             cfg.cls += ' active';
27155         }
27156         if(this.disabled){
27157             cfg.cls += ' disabled';
27158         }
27159         
27160         return cfg;
27161     },
27162     
27163     disable : function()
27164     {
27165         this.setDisabled(true);
27166     },
27167     
27168     enable : function()
27169     {
27170         this.setDisabled(false);
27171     },
27172     
27173     initEvents: function() 
27174     {
27175         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27176         
27177         this.iconEl.on('click', this.onClick, this);
27178     },
27179     
27180     onClick : function(e)
27181     {
27182         e.preventDefault();
27183         
27184         if(this.disabled){
27185             return;
27186         }
27187         
27188         if(this.fireEvent('click', this, e) === false){
27189             return;
27190         };
27191         
27192         this.parent().setActiveItem(this);
27193     },
27194     
27195     isActive: function () 
27196     {
27197         return this.active;
27198     },
27199     
27200     setActive : function(state)
27201     {
27202         if(this.active == state){
27203             return;
27204         }
27205         
27206         this.active = state;
27207         
27208         if (state) {
27209             this.el.addClass('active');
27210             return;
27211         }
27212         
27213         this.el.removeClass('active');
27214         
27215         return;
27216     },
27217     
27218     setDisabled : function(state)
27219     {
27220         if(this.disabled == state){
27221             return;
27222         }
27223         
27224         this.disabled = state;
27225         
27226         if (state) {
27227             this.el.addClass('disabled');
27228             return;
27229         }
27230         
27231         this.el.removeClass('disabled');
27232     },
27233     
27234     tooltipEl : function()
27235     {
27236         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27237     }
27238 });
27239  
27240
27241