8c7fee539ed5a4ccfa828ed096edf1c4c99dbdd2
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @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)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     
8042     getItems : function()
8043     {
8044         var r=new Roo.util.MixedCollection(false, function(o){
8045             return o.id || (o.id = Roo.id());
8046         });
8047         var iter = function(el) {
8048             if (el.inputEl) {
8049                 r.add(el);
8050             }
8051             if (!el.items) {
8052                 return;
8053             }
8054             Roo.each(el.items,function(e) {
8055                 iter(e);
8056             });
8057
8058
8059         };
8060
8061         iter(this);
8062         return r;
8063         
8064     }
8065
8066 });
8067
8068 Roo.apply(Roo.bootstrap.Form, {
8069     
8070     popover : {
8071         
8072         padding : 5,
8073         
8074         isApplied : false,
8075         
8076         isMasked : false,
8077         
8078         form : false,
8079         
8080         target : false,
8081         
8082         toolTip : false,
8083         
8084         intervalID : false,
8085         
8086         maskEl : false,
8087         
8088         apply : function()
8089         {
8090             if(this.isApplied){
8091                 return;
8092             }
8093             
8094             this.maskEl = {
8095                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8096                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8097                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8098                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8099             };
8100             
8101             this.maskEl.top.enableDisplayMode("block");
8102             this.maskEl.left.enableDisplayMode("block");
8103             this.maskEl.bottom.enableDisplayMode("block");
8104             this.maskEl.right.enableDisplayMode("block");
8105             
8106             this.toolTip = new Roo.bootstrap.Tooltip({
8107                 cls : 'roo-form-error-popover',
8108                 alignment : {
8109                     'left' : ['r-l', [-2,0], 'right'],
8110                     'right' : ['l-r', [2,0], 'left'],
8111                     'bottom' : ['tl-bl', [0,2], 'top'],
8112                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8113                 }
8114             });
8115             
8116             this.toolTip.render(Roo.get(document.body));
8117
8118             this.toolTip.el.enableDisplayMode("block");
8119             
8120             Roo.get(document.body).on('click', function(){
8121                 this.unmask();
8122             }, this);
8123             
8124             Roo.get(document.body).on('touchstart', function(){
8125                 this.unmask();
8126             }, this);
8127             
8128             this.isApplied = true
8129         },
8130         
8131         mask : function(form, target)
8132         {
8133             this.form = form;
8134             
8135             this.target = target;
8136             
8137             if(!this.form.errorMask || !target.el){
8138                 return;
8139             }
8140             
8141             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8142             
8143             Roo.log(scrollable);
8144             
8145             var ot = this.target.el.calcOffsetsTo(scrollable);
8146             
8147             var scrollTo = ot[1] - this.form.maskOffset;
8148             
8149             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8150             
8151             scrollable.scrollTo('top', scrollTo);
8152             
8153             var box = this.target.el.getBox();
8154             Roo.log(box);
8155             var zIndex = Roo.bootstrap.Modal.zIndex++;
8156
8157             
8158             this.maskEl.top.setStyle('position', 'absolute');
8159             this.maskEl.top.setStyle('z-index', zIndex);
8160             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8161             this.maskEl.top.setLeft(0);
8162             this.maskEl.top.setTop(0);
8163             this.maskEl.top.show();
8164             
8165             this.maskEl.left.setStyle('position', 'absolute');
8166             this.maskEl.left.setStyle('z-index', zIndex);
8167             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8168             this.maskEl.left.setLeft(0);
8169             this.maskEl.left.setTop(box.y - this.padding);
8170             this.maskEl.left.show();
8171
8172             this.maskEl.bottom.setStyle('position', 'absolute');
8173             this.maskEl.bottom.setStyle('z-index', zIndex);
8174             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8175             this.maskEl.bottom.setLeft(0);
8176             this.maskEl.bottom.setTop(box.bottom + this.padding);
8177             this.maskEl.bottom.show();
8178
8179             this.maskEl.right.setStyle('position', 'absolute');
8180             this.maskEl.right.setStyle('z-index', zIndex);
8181             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8182             this.maskEl.right.setLeft(box.right + this.padding);
8183             this.maskEl.right.setTop(box.y - this.padding);
8184             this.maskEl.right.show();
8185
8186             this.toolTip.bindEl = this.target.el;
8187
8188             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8189
8190             var tip = this.target.blankText;
8191
8192             if(this.target.getValue() !== '' ) {
8193                 
8194                 if (this.target.invalidText.length) {
8195                     tip = this.target.invalidText;
8196                 } else if (this.target.regexText.length){
8197                     tip = this.target.regexText;
8198                 }
8199             }
8200
8201             this.toolTip.show(tip);
8202
8203             this.intervalID = window.setInterval(function() {
8204                 Roo.bootstrap.Form.popover.unmask();
8205             }, 10000);
8206
8207             window.onwheel = function(){ return false;};
8208             
8209             (function(){ this.isMasked = true; }).defer(500, this);
8210             
8211         },
8212         
8213         unmask : function()
8214         {
8215             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8216                 return;
8217             }
8218             
8219             this.maskEl.top.setStyle('position', 'absolute');
8220             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.top.hide();
8222
8223             this.maskEl.left.setStyle('position', 'absolute');
8224             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.left.hide();
8226
8227             this.maskEl.bottom.setStyle('position', 'absolute');
8228             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8229             this.maskEl.bottom.hide();
8230
8231             this.maskEl.right.setStyle('position', 'absolute');
8232             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8233             this.maskEl.right.hide();
8234             
8235             this.toolTip.hide();
8236             
8237             this.toolTip.el.hide();
8238             
8239             window.onwheel = function(){ return true;};
8240             
8241             if(this.intervalID){
8242                 window.clearInterval(this.intervalID);
8243                 this.intervalID = false;
8244             }
8245             
8246             this.isMasked = false;
8247             
8248         }
8249         
8250     }
8251     
8252 });
8253
8254 /*
8255  * Based on:
8256  * Ext JS Library 1.1.1
8257  * Copyright(c) 2006-2007, Ext JS, LLC.
8258  *
8259  * Originally Released Under LGPL - original licence link has changed is not relivant.
8260  *
8261  * Fork - LGPL
8262  * <script type="text/javascript">
8263  */
8264 /**
8265  * @class Roo.form.VTypes
8266  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8267  * @singleton
8268  */
8269 Roo.form.VTypes = function(){
8270     // closure these in so they are only created once.
8271     var alpha = /^[a-zA-Z_]+$/;
8272     var alphanum = /^[a-zA-Z0-9_]+$/;
8273     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8274     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8275
8276     // All these messages and functions are configurable
8277     return {
8278         /**
8279          * The function used to validate email addresses
8280          * @param {String} value The email address
8281          */
8282         'email' : function(v){
8283             return email.test(v);
8284         },
8285         /**
8286          * The error text to display when the email validation function returns false
8287          * @type String
8288          */
8289         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8290         /**
8291          * The keystroke filter mask to be applied on email input
8292          * @type RegExp
8293          */
8294         'emailMask' : /[a-z0-9_\.\-@]/i,
8295
8296         /**
8297          * The function used to validate URLs
8298          * @param {String} value The URL
8299          */
8300         'url' : function(v){
8301             return url.test(v);
8302         },
8303         /**
8304          * The error text to display when the url validation function returns false
8305          * @type String
8306          */
8307         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8308         
8309         /**
8310          * The function used to validate alpha values
8311          * @param {String} value The value
8312          */
8313         'alpha' : function(v){
8314             return alpha.test(v);
8315         },
8316         /**
8317          * The error text to display when the alpha validation function returns false
8318          * @type String
8319          */
8320         'alphaText' : 'This field should only contain letters and _',
8321         /**
8322          * The keystroke filter mask to be applied on alpha input
8323          * @type RegExp
8324          */
8325         'alphaMask' : /[a-z_]/i,
8326
8327         /**
8328          * The function used to validate alphanumeric values
8329          * @param {String} value The value
8330          */
8331         'alphanum' : function(v){
8332             return alphanum.test(v);
8333         },
8334         /**
8335          * The error text to display when the alphanumeric validation function returns false
8336          * @type String
8337          */
8338         'alphanumText' : 'This field should only contain letters, numbers and _',
8339         /**
8340          * The keystroke filter mask to be applied on alphanumeric input
8341          * @type RegExp
8342          */
8343         'alphanumMask' : /[a-z0-9_]/i
8344     };
8345 }();/*
8346  * - LGPL
8347  *
8348  * Input
8349  * 
8350  */
8351
8352 /**
8353  * @class Roo.bootstrap.Input
8354  * @extends Roo.bootstrap.Component
8355  * Bootstrap Input class
8356  * @cfg {Boolean} disabled is it disabled
8357  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8358  * @cfg {String} name name of the input
8359  * @cfg {string} fieldLabel - the label associated
8360  * @cfg {string} placeholder - placeholder to put in text.
8361  * @cfg {string}  before - input group add on before
8362  * @cfg {string} after - input group add on after
8363  * @cfg {string} size - (lg|sm) or leave empty..
8364  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8365  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8366  * @cfg {Number} md colspan out of 12 for computer-sized screens
8367  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8368  * @cfg {string} value default value of the input
8369  * @cfg {Number} labelWidth set the width of label 
8370  * @cfg {Number} labellg set the width of label (1-12)
8371  * @cfg {Number} labelmd set the width of label (1-12)
8372  * @cfg {Number} labelsm set the width of label (1-12)
8373  * @cfg {Number} labelxs set the width of label (1-12)
8374  * @cfg {String} labelAlign (top|left)
8375  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8376  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8377  * @cfg {String} indicatorpos (left|right) default left
8378
8379  * @cfg {String} align (left|center|right) Default left
8380  * @cfg {Boolean} forceFeedback (true|false) Default false
8381  * 
8382  * 
8383  * 
8384  * 
8385  * @constructor
8386  * Create a new Input
8387  * @param {Object} config The config object
8388  */
8389
8390 Roo.bootstrap.Input = function(config){
8391     
8392     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8393     
8394     this.addEvents({
8395         /**
8396          * @event focus
8397          * Fires when this field receives input focus.
8398          * @param {Roo.form.Field} this
8399          */
8400         focus : true,
8401         /**
8402          * @event blur
8403          * Fires when this field loses input focus.
8404          * @param {Roo.form.Field} this
8405          */
8406         blur : true,
8407         /**
8408          * @event specialkey
8409          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8410          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8411          * @param {Roo.form.Field} this
8412          * @param {Roo.EventObject} e The event object
8413          */
8414         specialkey : true,
8415         /**
8416          * @event change
8417          * Fires just before the field blurs if the field value has changed.
8418          * @param {Roo.form.Field} this
8419          * @param {Mixed} newValue The new value
8420          * @param {Mixed} oldValue The original value
8421          */
8422         change : true,
8423         /**
8424          * @event invalid
8425          * Fires after the field has been marked as invalid.
8426          * @param {Roo.form.Field} this
8427          * @param {String} msg The validation message
8428          */
8429         invalid : true,
8430         /**
8431          * @event valid
8432          * Fires after the field has been validated with no errors.
8433          * @param {Roo.form.Field} this
8434          */
8435         valid : true,
8436          /**
8437          * @event keyup
8438          * Fires after the key up
8439          * @param {Roo.form.Field} this
8440          * @param {Roo.EventObject}  e The event Object
8441          */
8442         keyup : true
8443     });
8444 };
8445
8446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8447      /**
8448      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8449       automatic validation (defaults to "keyup").
8450      */
8451     validationEvent : "keyup",
8452      /**
8453      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8454      */
8455     validateOnBlur : true,
8456     /**
8457      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8458      */
8459     validationDelay : 250,
8460      /**
8461      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8462      */
8463     focusClass : "x-form-focus",  // not needed???
8464     
8465        
8466     /**
8467      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8468      */
8469     invalidClass : "has-warning",
8470     
8471     /**
8472      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8473      */
8474     validClass : "has-success",
8475     
8476     /**
8477      * @cfg {Boolean} hasFeedback (true|false) default true
8478      */
8479     hasFeedback : true,
8480     
8481     /**
8482      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8483      */
8484     invalidFeedbackClass : "glyphicon-warning-sign",
8485     
8486     /**
8487      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8488      */
8489     validFeedbackClass : "glyphicon-ok",
8490     
8491     /**
8492      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8493      */
8494     selectOnFocus : false,
8495     
8496      /**
8497      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8498      */
8499     maskRe : null,
8500        /**
8501      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8502      */
8503     vtype : null,
8504     
8505       /**
8506      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8507      */
8508     disableKeyFilter : false,
8509     
8510        /**
8511      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8512      */
8513     disabled : false,
8514      /**
8515      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8516      */
8517     allowBlank : true,
8518     /**
8519      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8520      */
8521     blankText : "Please complete this mandatory field",
8522     
8523      /**
8524      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8525      */
8526     minLength : 0,
8527     /**
8528      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8529      */
8530     maxLength : Number.MAX_VALUE,
8531     /**
8532      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8533      */
8534     minLengthText : "The minimum length for this field is {0}",
8535     /**
8536      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8537      */
8538     maxLengthText : "The maximum length for this field is {0}",
8539   
8540     
8541     /**
8542      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8543      * If available, this function will be called only after the basic validators all return true, and will be passed the
8544      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8545      */
8546     validator : null,
8547     /**
8548      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8549      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8550      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8551      */
8552     regex : null,
8553     /**
8554      * @cfg {String} regexText -- Depricated - use Invalid Text
8555      */
8556     regexText : "",
8557     
8558     /**
8559      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8560      */
8561     invalidText : "",
8562     
8563     
8564     
8565     autocomplete: false,
8566     
8567     
8568     fieldLabel : '',
8569     inputType : 'text',
8570     
8571     name : false,
8572     placeholder: false,
8573     before : false,
8574     after : false,
8575     size : false,
8576     hasFocus : false,
8577     preventMark: false,
8578     isFormField : true,
8579     value : '',
8580     labelWidth : 2,
8581     labelAlign : false,
8582     readOnly : false,
8583     align : false,
8584     formatedValue : false,
8585     forceFeedback : false,
8586     
8587     indicatorpos : 'left',
8588     
8589     labellg : 0,
8590     labelmd : 0,
8591     labelsm : 0,
8592     labelxs : 0,
8593     
8594     parentLabelAlign : function()
8595     {
8596         var parent = this;
8597         while (parent.parent()) {
8598             parent = parent.parent();
8599             if (typeof(parent.labelAlign) !='undefined') {
8600                 return parent.labelAlign;
8601             }
8602         }
8603         return 'left';
8604         
8605     },
8606     
8607     getAutoCreate : function()
8608     {
8609         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8610         
8611         var id = Roo.id();
8612         
8613         var cfg = {};
8614         
8615         if(this.inputType != 'hidden'){
8616             cfg.cls = 'form-group' //input-group
8617         }
8618         
8619         var input =  {
8620             tag: 'input',
8621             id : id,
8622             type : this.inputType,
8623             value : this.value,
8624             cls : 'form-control',
8625             placeholder : this.placeholder || '',
8626             autocomplete : this.autocomplete || 'new-password'
8627         };
8628         
8629         if(this.align){
8630             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8631         }
8632         
8633         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8634             input.maxLength = this.maxLength;
8635         }
8636         
8637         if (this.disabled) {
8638             input.disabled=true;
8639         }
8640         
8641         if (this.readOnly) {
8642             input.readonly=true;
8643         }
8644         
8645         if (this.name) {
8646             input.name = this.name;
8647         }
8648         
8649         if (this.size) {
8650             input.cls += ' input-' + this.size;
8651         }
8652         
8653         var settings=this;
8654         ['xs','sm','md','lg'].map(function(size){
8655             if (settings[size]) {
8656                 cfg.cls += ' col-' + size + '-' + settings[size];
8657             }
8658         });
8659         
8660         var inputblock = input;
8661         
8662         var feedback = {
8663             tag: 'span',
8664             cls: 'glyphicon form-control-feedback'
8665         };
8666             
8667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8668             
8669             inputblock = {
8670                 cls : 'has-feedback',
8671                 cn :  [
8672                     input,
8673                     feedback
8674                 ] 
8675             };  
8676         }
8677         
8678         if (this.before || this.after) {
8679             
8680             inputblock = {
8681                 cls : 'input-group',
8682                 cn :  [] 
8683             };
8684             
8685             if (this.before && typeof(this.before) == 'string') {
8686                 
8687                 inputblock.cn.push({
8688                     tag :'span',
8689                     cls : 'roo-input-before input-group-addon',
8690                     html : this.before
8691                 });
8692             }
8693             if (this.before && typeof(this.before) == 'object') {
8694                 this.before = Roo.factory(this.before);
8695                 
8696                 inputblock.cn.push({
8697                     tag :'span',
8698                     cls : 'roo-input-before input-group-' +
8699                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8700                 });
8701             }
8702             
8703             inputblock.cn.push(input);
8704             
8705             if (this.after && typeof(this.after) == 'string') {
8706                 inputblock.cn.push({
8707                     tag :'span',
8708                     cls : 'roo-input-after input-group-addon',
8709                     html : this.after
8710                 });
8711             }
8712             if (this.after && typeof(this.after) == 'object') {
8713                 this.after = Roo.factory(this.after);
8714                 
8715                 inputblock.cn.push({
8716                     tag :'span',
8717                     cls : 'roo-input-after input-group-' +
8718                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8719                 });
8720             }
8721             
8722             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8723                 inputblock.cls += ' has-feedback';
8724                 inputblock.cn.push(feedback);
8725             }
8726         };
8727         
8728         if (align ==='left' && this.fieldLabel.length) {
8729             
8730             cfg.cls += ' roo-form-group-label-left';
8731             
8732             cfg.cn = [
8733                 {
8734                     tag : 'i',
8735                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8736                     tooltip : 'This field is required'
8737                 },
8738                 {
8739                     tag: 'label',
8740                     'for' :  id,
8741                     cls : 'control-label',
8742                     html : this.fieldLabel
8743
8744                 },
8745                 {
8746                     cls : "", 
8747                     cn: [
8748                         inputblock
8749                     ]
8750                 }
8751             ];
8752             
8753             var labelCfg = cfg.cn[1];
8754             var contentCfg = cfg.cn[2];
8755             
8756             if(this.indicatorpos == 'right'){
8757                 cfg.cn = [
8758                     {
8759                         tag: 'label',
8760                         'for' :  id,
8761                         cls : 'control-label',
8762                         cn : [
8763                             {
8764                                 tag : 'span',
8765                                 html : this.fieldLabel
8766                             },
8767                             {
8768                                 tag : 'i',
8769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8770                                 tooltip : 'This field is required'
8771                             }
8772                         ]
8773                     },
8774                     {
8775                         cls : "",
8776                         cn: [
8777                             inputblock
8778                         ]
8779                     }
8780
8781                 ];
8782                 
8783                 labelCfg = cfg.cn[0];
8784                 contentCfg = cfg.cn[1];
8785             
8786             }
8787             
8788             if(this.labelWidth > 12){
8789                 labelCfg.style = "width: " + this.labelWidth + 'px';
8790             }
8791             
8792             if(this.labelWidth < 13 && this.labelmd == 0){
8793                 this.labelmd = this.labelWidth;
8794             }
8795             
8796             if(this.labellg > 0){
8797                 labelCfg.cls += ' col-lg-' + this.labellg;
8798                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8799             }
8800             
8801             if(this.labelmd > 0){
8802                 labelCfg.cls += ' col-md-' + this.labelmd;
8803                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8804             }
8805             
8806             if(this.labelsm > 0){
8807                 labelCfg.cls += ' col-sm-' + this.labelsm;
8808                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8809             }
8810             
8811             if(this.labelxs > 0){
8812                 labelCfg.cls += ' col-xs-' + this.labelxs;
8813                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8814             }
8815             
8816             
8817         } else if ( this.fieldLabel.length) {
8818                 
8819             cfg.cn = [
8820                 {
8821                     tag : 'i',
8822                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8823                     tooltip : 'This field is required'
8824                 },
8825                 {
8826                     tag: 'label',
8827                    //cls : 'input-group-addon',
8828                     html : this.fieldLabel
8829
8830                 },
8831
8832                inputblock
8833
8834            ];
8835            
8836            if(this.indicatorpos == 'right'){
8837                 
8838                 cfg.cn = [
8839                     {
8840                         tag: 'label',
8841                        //cls : 'input-group-addon',
8842                         html : this.fieldLabel
8843
8844                     },
8845                     {
8846                         tag : 'i',
8847                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8848                         tooltip : 'This field is required'
8849                     },
8850
8851                    inputblock
8852
8853                ];
8854
8855             }
8856
8857         } else {
8858             
8859             cfg.cn = [
8860
8861                     inputblock
8862
8863             ];
8864                 
8865                 
8866         };
8867         
8868         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8869            cfg.cls += ' navbar-form';
8870         }
8871         
8872         if (this.parentType === 'NavGroup') {
8873            cfg.cls += ' navbar-form';
8874            cfg.tag = 'li';
8875         }
8876         
8877         return cfg;
8878         
8879     },
8880     /**
8881      * return the real input element.
8882      */
8883     inputEl: function ()
8884     {
8885         return this.el.select('input.form-control',true).first();
8886     },
8887     
8888     tooltipEl : function()
8889     {
8890         return this.inputEl();
8891     },
8892     
8893     indicatorEl : function()
8894     {
8895         var indicator = this.el.select('i.roo-required-indicator',true).first();
8896         
8897         if(!indicator){
8898             return false;
8899         }
8900         
8901         return indicator;
8902         
8903     },
8904     
8905     setDisabled : function(v)
8906     {
8907         var i  = this.inputEl().dom;
8908         if (!v) {
8909             i.removeAttribute('disabled');
8910             return;
8911             
8912         }
8913         i.setAttribute('disabled','true');
8914     },
8915     initEvents : function()
8916     {
8917           
8918         this.inputEl().on("keydown" , this.fireKey,  this);
8919         this.inputEl().on("focus", this.onFocus,  this);
8920         this.inputEl().on("blur", this.onBlur,  this);
8921         
8922         this.inputEl().relayEvent('keyup', this);
8923         
8924         this.indicator = this.indicatorEl();
8925         
8926         if(this.indicator){
8927             this.indicator.addClass('invisible');
8928             
8929         }
8930  
8931         // reference to original value for reset
8932         this.originalValue = this.getValue();
8933         //Roo.form.TextField.superclass.initEvents.call(this);
8934         if(this.validationEvent == 'keyup'){
8935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8936             this.inputEl().on('keyup', this.filterValidation, this);
8937         }
8938         else if(this.validationEvent !== false){
8939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8940         }
8941         
8942         if(this.selectOnFocus){
8943             this.on("focus", this.preFocus, this);
8944             
8945         }
8946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8947             this.inputEl().on("keypress", this.filterKeys, this);
8948         } else {
8949             this.inputEl().relayEvent('keypress', this);
8950         }
8951        /* if(this.grow){
8952             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8953             this.el.on("click", this.autoSize,  this);
8954         }
8955         */
8956         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8957             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8958         }
8959         
8960         if (typeof(this.before) == 'object') {
8961             this.before.render(this.el.select('.roo-input-before',true).first());
8962         }
8963         if (typeof(this.after) == 'object') {
8964             this.after.render(this.el.select('.roo-input-after',true).first());
8965         }
8966         
8967         
8968     },
8969     filterValidation : function(e){
8970         if(!e.isNavKeyPress()){
8971             this.validationTask.delay(this.validationDelay);
8972         }
8973     },
8974      /**
8975      * Validates the field value
8976      * @return {Boolean} True if the value is valid, else false
8977      */
8978     validate : function(){
8979         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8980         if(this.disabled || this.validateValue(this.getRawValue())){
8981             this.markValid();
8982             return true;
8983         }
8984         
8985         this.markInvalid();
8986         return false;
8987     },
8988     
8989     
8990     /**
8991      * Validates a value according to the field's validation rules and marks the field as invalid
8992      * if the validation fails
8993      * @param {Mixed} value The value to validate
8994      * @return {Boolean} True if the value is valid, else false
8995      */
8996     validateValue : function(value){
8997         if(value.length < 1)  { // if it's blank
8998             if(this.allowBlank){
8999                 return true;
9000             }            
9001             return this.inputEl().hasClass('hide') ? true : false;
9002         }
9003         
9004         if(value.length < this.minLength){
9005             return false;
9006         }
9007         if(value.length > this.maxLength){
9008             return false;
9009         }
9010         if(this.vtype){
9011             var vt = Roo.form.VTypes;
9012             if(!vt[this.vtype](value, this)){
9013                 return false;
9014             }
9015         }
9016         if(typeof this.validator == "function"){
9017             var msg = this.validator(value);
9018             if(msg !== true){
9019                 return false;
9020             }
9021             if (typeof(msg) == 'string') {
9022                 this.invalidText = msg;
9023             }
9024         }
9025         
9026         if(this.regex && !this.regex.test(value)){
9027             return false;
9028         }
9029         
9030         return true;
9031     },
9032
9033     
9034     
9035      // private
9036     fireKey : function(e){
9037         //Roo.log('field ' + e.getKey());
9038         if(e.isNavKeyPress()){
9039             this.fireEvent("specialkey", this, e);
9040         }
9041     },
9042     focus : function (selectText){
9043         if(this.rendered){
9044             this.inputEl().focus();
9045             if(selectText === true){
9046                 this.inputEl().dom.select();
9047             }
9048         }
9049         return this;
9050     } ,
9051     
9052     onFocus : function(){
9053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054            // this.el.addClass(this.focusClass);
9055         }
9056         if(!this.hasFocus){
9057             this.hasFocus = true;
9058             this.startValue = this.getValue();
9059             this.fireEvent("focus", this);
9060         }
9061     },
9062     
9063     beforeBlur : Roo.emptyFn,
9064
9065     
9066     // private
9067     onBlur : function(){
9068         this.beforeBlur();
9069         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9070             //this.el.removeClass(this.focusClass);
9071         }
9072         this.hasFocus = false;
9073         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9074             this.validate();
9075         }
9076         var v = this.getValue();
9077         if(String(v) !== String(this.startValue)){
9078             this.fireEvent('change', this, v, this.startValue);
9079         }
9080         this.fireEvent("blur", this);
9081     },
9082     
9083     /**
9084      * Resets the current field value to the originally loaded value and clears any validation messages
9085      */
9086     reset : function(){
9087         this.setValue(this.originalValue);
9088         this.validate();
9089     },
9090      /**
9091      * Returns the name of the field
9092      * @return {Mixed} name The name field
9093      */
9094     getName: function(){
9095         return this.name;
9096     },
9097      /**
9098      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9099      * @return {Mixed} value The field value
9100      */
9101     getValue : function(){
9102         
9103         var v = this.inputEl().getValue();
9104         
9105         return v;
9106     },
9107     /**
9108      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9109      * @return {Mixed} value The field value
9110      */
9111     getRawValue : function(){
9112         var v = this.inputEl().getValue();
9113         
9114         return v;
9115     },
9116     
9117     /**
9118      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9119      * @param {Mixed} value The value to set
9120      */
9121     setRawValue : function(v){
9122         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9123     },
9124     
9125     selectText : function(start, end){
9126         var v = this.getRawValue();
9127         if(v.length > 0){
9128             start = start === undefined ? 0 : start;
9129             end = end === undefined ? v.length : end;
9130             var d = this.inputEl().dom;
9131             if(d.setSelectionRange){
9132                 d.setSelectionRange(start, end);
9133             }else if(d.createTextRange){
9134                 var range = d.createTextRange();
9135                 range.moveStart("character", start);
9136                 range.moveEnd("character", v.length-end);
9137                 range.select();
9138             }
9139         }
9140     },
9141     
9142     /**
9143      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9144      * @param {Mixed} value The value to set
9145      */
9146     setValue : function(v){
9147         this.value = v;
9148         if(this.rendered){
9149             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9150             this.validate();
9151         }
9152     },
9153     
9154     /*
9155     processValue : function(value){
9156         if(this.stripCharsRe){
9157             var newValue = value.replace(this.stripCharsRe, '');
9158             if(newValue !== value){
9159                 this.setRawValue(newValue);
9160                 return newValue;
9161             }
9162         }
9163         return value;
9164     },
9165   */
9166     preFocus : function(){
9167         
9168         if(this.selectOnFocus){
9169             this.inputEl().dom.select();
9170         }
9171     },
9172     filterKeys : function(e){
9173         var k = e.getKey();
9174         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9175             return;
9176         }
9177         var c = e.getCharCode(), cc = String.fromCharCode(c);
9178         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9179             return;
9180         }
9181         if(!this.maskRe.test(cc)){
9182             e.stopEvent();
9183         }
9184     },
9185      /**
9186      * Clear any invalid styles/messages for this field
9187      */
9188     clearInvalid : function(){
9189         
9190         if(!this.el || this.preventMark){ // not rendered
9191             return;
9192         }
9193         
9194      
9195         this.el.removeClass(this.invalidClass);
9196         
9197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9198             
9199             var feedback = this.el.select('.form-control-feedback', true).first();
9200             
9201             if(feedback){
9202                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as valid
9212      */
9213     markValid : function()
9214     {
9215         if(!this.el  || this.preventMark){ // not rendered...
9216             return;
9217         }
9218         
9219         this.el.removeClass([this.invalidClass, this.validClass]);
9220         
9221         var feedback = this.el.select('.form-control-feedback', true).first();
9222             
9223         if(feedback){
9224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9225         }
9226
9227         if(this.disabled){
9228             return;
9229         }
9230         
9231         if(this.allowBlank && !this.getRawValue().length){
9232             return;
9233         }
9234         
9235         if(this.indicator){
9236             this.indicator.removeClass('visible');
9237             this.indicator.addClass('invisible');
9238         }
9239         
9240         this.el.addClass(this.validClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9249             }
9250             
9251         }
9252         
9253         this.fireEvent('valid', this);
9254     },
9255     
9256      /**
9257      * Mark this field as invalid
9258      * @param {String} msg The validation message
9259      */
9260     markInvalid : function(msg)
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273
9274         if(this.disabled){
9275             return;
9276         }
9277         
9278         if(this.allowBlank && !this.getRawValue().length){
9279             return;
9280         }
9281         
9282         if(this.indicator){
9283             this.indicator.removeClass('invisible');
9284             this.indicator.addClass('visible');
9285         }
9286         
9287         this.el.addClass(this.invalidClass);
9288         
9289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9290             
9291             var feedback = this.el.select('.form-control-feedback', true).first();
9292             
9293             if(feedback){
9294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9295                 
9296                 if(this.getValue().length || this.forceFeedback){
9297                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9298                 }
9299                 
9300             }
9301             
9302         }
9303         
9304         this.fireEvent('invalid', this, msg);
9305     },
9306     // private
9307     SafariOnKeyDown : function(event)
9308     {
9309         // this is a workaround for a password hang bug on chrome/ webkit.
9310         if (this.inputEl().dom.type != 'password') {
9311             return;
9312         }
9313         
9314         var isSelectAll = false;
9315         
9316         if(this.inputEl().dom.selectionEnd > 0){
9317             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9318         }
9319         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9320             event.preventDefault();
9321             this.setValue('');
9322             return;
9323         }
9324         
9325         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9326             
9327             event.preventDefault();
9328             // this is very hacky as keydown always get's upper case.
9329             //
9330             var cc = String.fromCharCode(event.getCharCode());
9331             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9332             
9333         }
9334     },
9335     adjustWidth : function(tag, w){
9336         tag = tag.toLowerCase();
9337         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9338             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9339                 if(tag == 'input'){
9340                     return w + 2;
9341                 }
9342                 if(tag == 'textarea'){
9343                     return w-2;
9344                 }
9345             }else if(Roo.isOpera){
9346                 if(tag == 'input'){
9347                     return w + 2;
9348                 }
9349                 if(tag == 'textarea'){
9350                     return w-2;
9351                 }
9352             }
9353         }
9354         return w;
9355     },
9356     
9357     setFieldLabel : function(v)
9358     {
9359         if(!this.rendered){
9360             return;
9361         }
9362         
9363         this.fieldLabel = v;
9364         
9365         if(this.indicator){
9366             var ar = this.el.select('label > span',true);
9367             if (!ar.length) {
9368                 Roo.log("could not find label > span on element");
9369                 Roo.log(this);
9370                 return;
9371             }
9372             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9373             return;
9374         }
9375         
9376         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9377     }
9378 });
9379
9380  
9381 /*
9382  * - LGPL
9383  *
9384  * Input
9385  * 
9386  */
9387
9388 /**
9389  * @class Roo.bootstrap.TextArea
9390  * @extends Roo.bootstrap.Input
9391  * Bootstrap TextArea class
9392  * @cfg {Number} cols Specifies the visible width of a text area
9393  * @cfg {Number} rows Specifies the visible number of lines in a text area
9394  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9395  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9396  * @cfg {string} html text
9397  * 
9398  * @constructor
9399  * Create a new TextArea
9400  * @param {Object} config The config object
9401  */
9402
9403 Roo.bootstrap.TextArea = function(config){
9404     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9405    
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9409      
9410     cols : false,
9411     rows : 5,
9412     readOnly : false,
9413     warp : 'soft',
9414     resize : false,
9415     value: false,
9416     html: false,
9417     
9418     getAutoCreate : function(){
9419         
9420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9421         
9422         var id = Roo.id();
9423         
9424         var cfg = {};
9425         
9426         if(this.inputType != 'hidden'){
9427             cfg.cls = 'form-group' //input-group
9428         }
9429         
9430         var input =  {
9431             tag: 'textarea',
9432             id : id,
9433             warp : this.warp,
9434             rows : this.rows,
9435             value : this.value || '',
9436             html: this.html || '',
9437             cls : 'form-control',
9438             placeholder : this.placeholder || '' 
9439             
9440         };
9441         
9442         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9443             input.maxLength = this.maxLength;
9444         }
9445         
9446         if(this.resize){
9447             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9448         }
9449         
9450         if(this.cols){
9451             input.cols = this.cols;
9452         }
9453         
9454         if (this.readOnly) {
9455             input.readonly = true;
9456         }
9457         
9458         if (this.name) {
9459             input.name = this.name;
9460         }
9461         
9462         if (this.size) {
9463             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9464         }
9465         
9466         var settings=this;
9467         ['xs','sm','md','lg'].map(function(size){
9468             if (settings[size]) {
9469                 cfg.cls += ' col-' + size + '-' + settings[size];
9470             }
9471         });
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481
9482             inputblock = {
9483                 cls : 'has-feedback',
9484                 cn :  [
9485                     input,
9486                     feedback
9487                 ] 
9488             };  
9489         }
9490         
9491         
9492         if (this.before || this.after) {
9493             
9494             inputblock = {
9495                 cls : 'input-group',
9496                 cn :  [] 
9497             };
9498             if (this.before) {
9499                 inputblock.cn.push({
9500                     tag :'span',
9501                     cls : 'input-group-addon',
9502                     html : this.before
9503                 });
9504             }
9505             
9506             inputblock.cn.push(input);
9507             
9508             if(this.hasFeedback && !this.allowBlank){
9509                 inputblock.cls += ' has-feedback';
9510                 inputblock.cn.push(feedback);
9511             }
9512             
9513             if (this.after) {
9514                 inputblock.cn.push({
9515                     tag :'span',
9516                     cls : 'input-group-addon',
9517                     html : this.after
9518                 });
9519             }
9520             
9521         }
9522         
9523         if (align ==='left' && this.fieldLabel.length) {
9524             cfg.cn = [
9525                 {
9526                     tag: 'label',
9527                     'for' :  id,
9528                     cls : 'control-label',
9529                     html : this.fieldLabel
9530                 },
9531                 {
9532                     cls : "",
9533                     cn: [
9534                         inputblock
9535                     ]
9536                 }
9537
9538             ];
9539             
9540             if(this.labelWidth > 12){
9541                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9542             }
9543
9544             if(this.labelWidth < 13 && this.labelmd == 0){
9545                 this.labelmd = this.labelWidth;
9546             }
9547
9548             if(this.labellg > 0){
9549                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9550                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9551             }
9552
9553             if(this.labelmd > 0){
9554                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9555                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9556             }
9557
9558             if(this.labelsm > 0){
9559                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9560                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9561             }
9562
9563             if(this.labelxs > 0){
9564                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9565                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9566             }
9567             
9568         } else if ( this.fieldLabel.length) {
9569             cfg.cn = [
9570
9571                {
9572                    tag: 'label',
9573                    //cls : 'input-group-addon',
9574                    html : this.fieldLabel
9575
9576                },
9577
9578                inputblock
9579
9580            ];
9581
9582         } else {
9583
9584             cfg.cn = [
9585
9586                 inputblock
9587
9588             ];
9589                 
9590         }
9591         
9592         if (this.disabled) {
9593             input.disabled=true;
9594         }
9595         
9596         return cfg;
9597         
9598     },
9599     /**
9600      * return the real textarea element.
9601      */
9602     inputEl: function ()
9603     {
9604         return this.el.select('textarea.form-control',true).first();
9605     },
9606     
9607     /**
9608      * Clear any invalid styles/messages for this field
9609      */
9610     clearInvalid : function()
9611     {
9612         
9613         if(!this.el || this.preventMark){ // not rendered
9614             return;
9615         }
9616         
9617         var label = this.el.select('label', true).first();
9618         var icon = this.el.select('i.fa-star', true).first();
9619         
9620         if(label && icon){
9621             icon.remove();
9622         }
9623         
9624         this.el.removeClass(this.invalidClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9632             }
9633             
9634         }
9635         
9636         this.fireEvent('valid', this);
9637     },
9638     
9639      /**
9640      * Mark this field as valid
9641      */
9642     markValid : function()
9643     {
9644         if(!this.el  || this.preventMark){ // not rendered
9645             return;
9646         }
9647         
9648         this.el.removeClass([this.invalidClass, this.validClass]);
9649         
9650         var feedback = this.el.select('.form-control-feedback', true).first();
9651             
9652         if(feedback){
9653             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9654         }
9655
9656         if(this.disabled || this.allowBlank){
9657             return;
9658         }
9659         
9660         var label = this.el.select('label', true).first();
9661         var icon = this.el.select('i.fa-star', true).first();
9662         
9663         if(label && icon){
9664             icon.remove();
9665         }
9666         
9667         this.el.addClass(this.validClass);
9668         
9669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9670             
9671             var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673             if(feedback){
9674                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9676             }
9677             
9678         }
9679         
9680         this.fireEvent('valid', this);
9681     },
9682     
9683      /**
9684      * Mark this field as invalid
9685      * @param {String} msg The validation message
9686      */
9687     markInvalid : function(msg)
9688     {
9689         if(!this.el  || this.preventMark){ // not rendered
9690             return;
9691         }
9692         
9693         this.el.removeClass([this.invalidClass, this.validClass]);
9694         
9695         var feedback = this.el.select('.form-control-feedback', true).first();
9696             
9697         if(feedback){
9698             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9699         }
9700
9701         if(this.disabled || this.allowBlank){
9702             return;
9703         }
9704         
9705         var label = this.el.select('label', true).first();
9706         var icon = this.el.select('i.fa-star', true).first();
9707         
9708         if(!this.getValue().length && label && !icon){
9709             this.el.createChild({
9710                 tag : 'i',
9711                 cls : 'text-danger fa fa-lg fa-star',
9712                 tooltip : 'This field is required',
9713                 style : 'margin-right:5px;'
9714             }, label, true);
9715         }
9716
9717         this.el.addClass(this.invalidClass);
9718         
9719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9720             
9721             var feedback = this.el.select('.form-control-feedback', true).first();
9722             
9723             if(feedback){
9724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725                 
9726                 if(this.getValue().length || this.forceFeedback){
9727                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9728                 }
9729                 
9730             }
9731             
9732         }
9733         
9734         this.fireEvent('invalid', this, msg);
9735     }
9736 });
9737
9738  
9739 /*
9740  * - LGPL
9741  *
9742  * trigger field - base class for combo..
9743  * 
9744  */
9745  
9746 /**
9747  * @class Roo.bootstrap.TriggerField
9748  * @extends Roo.bootstrap.Input
9749  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9750  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9751  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9752  * for which you can provide a custom implementation.  For example:
9753  * <pre><code>
9754 var trigger = new Roo.bootstrap.TriggerField();
9755 trigger.onTriggerClick = myTriggerFn;
9756 trigger.applyTo('my-field');
9757 </code></pre>
9758  *
9759  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9760  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9761  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9762  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9763  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9764
9765  * @constructor
9766  * Create a new TriggerField.
9767  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9768  * to the base TextField)
9769  */
9770 Roo.bootstrap.TriggerField = function(config){
9771     this.mimicing = false;
9772     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9773 };
9774
9775 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9776     /**
9777      * @cfg {String} triggerClass A CSS class to apply to the trigger
9778      */
9779      /**
9780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9781      */
9782     hideTrigger:false,
9783
9784     /**
9785      * @cfg {Boolean} removable (true|false) special filter default false
9786      */
9787     removable : false,
9788     
9789     /** @cfg {Boolean} grow @hide */
9790     /** @cfg {Number} growMin @hide */
9791     /** @cfg {Number} growMax @hide */
9792
9793     /**
9794      * @hide 
9795      * @method
9796      */
9797     autoSize: Roo.emptyFn,
9798     // private
9799     monitorTab : true,
9800     // private
9801     deferHeight : true,
9802
9803     
9804     actionMode : 'wrap',
9805     
9806     caret : false,
9807     
9808     
9809     getAutoCreate : function(){
9810        
9811         var align = this.labelAlign || this.parentLabelAlign();
9812         
9813         var id = Roo.id();
9814         
9815         var cfg = {
9816             cls: 'form-group' //input-group
9817         };
9818         
9819         
9820         var input =  {
9821             tag: 'input',
9822             id : id,
9823             type : this.inputType,
9824             cls : 'form-control',
9825             autocomplete: 'new-password',
9826             placeholder : this.placeholder || '' 
9827             
9828         };
9829         if (this.name) {
9830             input.name = this.name;
9831         }
9832         if (this.size) {
9833             input.cls += ' input-' + this.size;
9834         }
9835         
9836         if (this.disabled) {
9837             input.disabled=true;
9838         }
9839         
9840         var inputblock = input;
9841         
9842         if(this.hasFeedback && !this.allowBlank){
9843             
9844             var feedback = {
9845                 tag: 'span',
9846                 cls: 'glyphicon form-control-feedback'
9847             };
9848             
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'has-feedback',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         },
9859                         feedback
9860                     ] 
9861                 };
9862             } else {
9863                 inputblock = {
9864                     cls : 'has-feedback',
9865                     cn :  [
9866                         inputblock,
9867                         feedback
9868                     ] 
9869                 };
9870             }
9871
9872         } else {
9873             if(this.removable && !this.editable && !this.tickable){
9874                 inputblock = {
9875                     cls : 'roo-removable',
9876                     cn :  [
9877                         inputblock,
9878                         {
9879                             tag: 'button',
9880                             html : 'x',
9881                             cls : 'roo-combo-removable-btn close'
9882                         }
9883                     ] 
9884                 };
9885             }
9886         }
9887         
9888         if (this.before || this.after) {
9889             
9890             inputblock = {
9891                 cls : 'input-group',
9892                 cn :  [] 
9893             };
9894             if (this.before) {
9895                 inputblock.cn.push({
9896                     tag :'span',
9897                     cls : 'input-group-addon',
9898                     html : this.before
9899                 });
9900             }
9901             
9902             inputblock.cn.push(input);
9903             
9904             if(this.hasFeedback && !this.allowBlank){
9905                 inputblock.cls += ' has-feedback';
9906                 inputblock.cn.push(feedback);
9907             }
9908             
9909             if (this.after) {
9910                 inputblock.cn.push({
9911                     tag :'span',
9912                     cls : 'input-group-addon',
9913                     html : this.after
9914                 });
9915             }
9916             
9917         };
9918         
9919         var box = {
9920             tag: 'div',
9921             cn: [
9922                 {
9923                     tag: 'input',
9924                     type : 'hidden',
9925                     cls: 'form-hidden-field'
9926                 },
9927                 inputblock
9928             ]
9929             
9930         };
9931         
9932         if(this.multiple){
9933             box = {
9934                 tag: 'div',
9935                 cn: [
9936                     {
9937                         tag: 'input',
9938                         type : 'hidden',
9939                         cls: 'form-hidden-field'
9940                     },
9941                     {
9942                         tag: 'ul',
9943                         cls: 'roo-select2-choices',
9944                         cn:[
9945                             {
9946                                 tag: 'li',
9947                                 cls: 'roo-select2-search-field',
9948                                 cn: [
9949
9950                                     inputblock
9951                                 ]
9952                             }
9953                         ]
9954                     }
9955                 ]
9956             }
9957         };
9958         
9959         var combobox = {
9960             cls: 'roo-select2-container input-group',
9961             cn: [
9962                 box
9963 //                {
9964 //                    tag: 'ul',
9965 //                    cls: 'typeahead typeahead-long dropdown-menu',
9966 //                    style: 'display:none'
9967 //                }
9968             ]
9969         };
9970         
9971         if(!this.multiple && this.showToggleBtn){
9972             
9973             var caret = {
9974                         tag: 'span',
9975                         cls: 'caret'
9976              };
9977             if (this.caret != false) {
9978                 caret = {
9979                      tag: 'i',
9980                      cls: 'fa fa-' + this.caret
9981                 };
9982                 
9983             }
9984             
9985             combobox.cn.push({
9986                 tag :'span',
9987                 cls : 'input-group-addon btn dropdown-toggle',
9988                 cn : [
9989                     caret,
9990                     {
9991                         tag: 'span',
9992                         cls: 'combobox-clear',
9993                         cn  : [
9994                             {
9995                                 tag : 'i',
9996                                 cls: 'icon-remove'
9997                             }
9998                         ]
9999                     }
10000                 ]
10001
10002             })
10003         }
10004         
10005         if(this.multiple){
10006             combobox.cls += ' roo-select2-container-multi';
10007         }
10008         
10009         if (align ==='left' && this.fieldLabel.length) {
10010             
10011             cfg.cls += ' roo-form-group-label-left';
10012
10013             cfg.cn = [
10014                 {
10015                     tag : 'i',
10016                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10017                     tooltip : 'This field is required'
10018                 },
10019                 {
10020                     tag: 'label',
10021                     'for' :  id,
10022                     cls : 'control-label',
10023                     html : this.fieldLabel
10024
10025                 },
10026                 {
10027                     cls : "", 
10028                     cn: [
10029                         combobox
10030                     ]
10031                 }
10032
10033             ];
10034             
10035             var labelCfg = cfg.cn[1];
10036             var contentCfg = cfg.cn[2];
10037             
10038             if(this.indicatorpos == 'right'){
10039                 cfg.cn = [
10040                     {
10041                         tag: 'label',
10042                         'for' :  id,
10043                         cls : 'control-label',
10044                         cn : [
10045                             {
10046                                 tag : 'span',
10047                                 html : this.fieldLabel
10048                             },
10049                             {
10050                                 tag : 'i',
10051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10052                                 tooltip : 'This field is required'
10053                             }
10054                         ]
10055                     },
10056                     {
10057                         cls : "", 
10058                         cn: [
10059                             combobox
10060                         ]
10061                     }
10062
10063                 ];
10064                 
10065                 labelCfg = cfg.cn[0];
10066                 contentCfg = cfg.cn[1];
10067             }
10068             
10069             if(this.labelWidth > 12){
10070                 labelCfg.style = "width: " + this.labelWidth + 'px';
10071             }
10072             
10073             if(this.labelWidth < 13 && this.labelmd == 0){
10074                 this.labelmd = this.labelWidth;
10075             }
10076             
10077             if(this.labellg > 0){
10078                 labelCfg.cls += ' col-lg-' + this.labellg;
10079                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10080             }
10081             
10082             if(this.labelmd > 0){
10083                 labelCfg.cls += ' col-md-' + this.labelmd;
10084                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10085             }
10086             
10087             if(this.labelsm > 0){
10088                 labelCfg.cls += ' col-sm-' + this.labelsm;
10089                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10090             }
10091             
10092             if(this.labelxs > 0){
10093                 labelCfg.cls += ' col-xs-' + this.labelxs;
10094                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10095             }
10096             
10097         } else if ( this.fieldLabel.length) {
10098 //                Roo.log(" label");
10099             cfg.cn = [
10100                 {
10101                    tag : 'i',
10102                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10103                    tooltip : 'This field is required'
10104                },
10105                {
10106                    tag: 'label',
10107                    //cls : 'input-group-addon',
10108                    html : this.fieldLabel
10109
10110                },
10111
10112                combobox
10113
10114             ];
10115             
10116             if(this.indicatorpos == 'right'){
10117                 
10118                 cfg.cn = [
10119                     {
10120                        tag: 'label',
10121                        cn : [
10122                            {
10123                                tag : 'span',
10124                                html : this.fieldLabel
10125                            },
10126                            {
10127                               tag : 'i',
10128                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10129                               tooltip : 'This field is required'
10130                            }
10131                        ]
10132
10133                     },
10134                     combobox
10135
10136                 ];
10137
10138             }
10139
10140         } else {
10141             
10142 //                Roo.log(" no label && no align");
10143                 cfg = combobox
10144                      
10145                 
10146         }
10147         
10148         var settings=this;
10149         ['xs','sm','md','lg'].map(function(size){
10150             if (settings[size]) {
10151                 cfg.cls += ' col-' + size + '-' + settings[size];
10152             }
10153         });
10154         
10155         return cfg;
10156         
10157     },
10158     
10159     
10160     
10161     // private
10162     onResize : function(w, h){
10163 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10164 //        if(typeof w == 'number'){
10165 //            var x = w - this.trigger.getWidth();
10166 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10167 //            this.trigger.setStyle('left', x+'px');
10168 //        }
10169     },
10170
10171     // private
10172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10173
10174     // private
10175     getResizeEl : function(){
10176         return this.inputEl();
10177     },
10178
10179     // private
10180     getPositionEl : function(){
10181         return this.inputEl();
10182     },
10183
10184     // private
10185     alignErrorIcon : function(){
10186         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10187     },
10188
10189     // private
10190     initEvents : function(){
10191         
10192         this.createList();
10193         
10194         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10195         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10196         if(!this.multiple && this.showToggleBtn){
10197             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10198             if(this.hideTrigger){
10199                 this.trigger.setDisplayed(false);
10200             }
10201             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10202         }
10203         
10204         if(this.multiple){
10205             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10206         }
10207         
10208         if(this.removable && !this.editable && !this.tickable){
10209             var close = this.closeTriggerEl();
10210             
10211             if(close){
10212                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10213                 close.on('click', this.removeBtnClick, this, close);
10214             }
10215         }
10216         
10217         //this.trigger.addClassOnOver('x-form-trigger-over');
10218         //this.trigger.addClassOnClick('x-form-trigger-click');
10219         
10220         //if(!this.width){
10221         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10222         //}
10223     },
10224     
10225     closeTriggerEl : function()
10226     {
10227         var close = this.el.select('.roo-combo-removable-btn', true).first();
10228         return close ? close : false;
10229     },
10230     
10231     removeBtnClick : function(e, h, el)
10232     {
10233         e.preventDefault();
10234         
10235         if(this.fireEvent("remove", this) !== false){
10236             this.reset();
10237             this.fireEvent("afterremove", this)
10238         }
10239     },
10240     
10241     createList : function()
10242     {
10243         this.list = Roo.get(document.body).createChild({
10244             tag: 'ul',
10245             cls: 'typeahead typeahead-long dropdown-menu',
10246             style: 'display:none'
10247         });
10248         
10249         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10250         
10251     },
10252
10253     // private
10254     initTrigger : function(){
10255        
10256     },
10257
10258     // private
10259     onDestroy : function(){
10260         if(this.trigger){
10261             this.trigger.removeAllListeners();
10262           //  this.trigger.remove();
10263         }
10264         //if(this.wrap){
10265         //    this.wrap.remove();
10266         //}
10267         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10268     },
10269
10270     // private
10271     onFocus : function(){
10272         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10273         /*
10274         if(!this.mimicing){
10275             this.wrap.addClass('x-trigger-wrap-focus');
10276             this.mimicing = true;
10277             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10278             if(this.monitorTab){
10279                 this.el.on("keydown", this.checkTab, this);
10280             }
10281         }
10282         */
10283     },
10284
10285     // private
10286     checkTab : function(e){
10287         if(e.getKey() == e.TAB){
10288             this.triggerBlur();
10289         }
10290     },
10291
10292     // private
10293     onBlur : function(){
10294         // do nothing
10295     },
10296
10297     // private
10298     mimicBlur : function(e, t){
10299         /*
10300         if(!this.wrap.contains(t) && this.validateBlur()){
10301             this.triggerBlur();
10302         }
10303         */
10304     },
10305
10306     // private
10307     triggerBlur : function(){
10308         this.mimicing = false;
10309         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10310         if(this.monitorTab){
10311             this.el.un("keydown", this.checkTab, this);
10312         }
10313         //this.wrap.removeClass('x-trigger-wrap-focus');
10314         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10315     },
10316
10317     // private
10318     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10319     validateBlur : function(e, t){
10320         return true;
10321     },
10322
10323     // private
10324     onDisable : function(){
10325         this.inputEl().dom.disabled = true;
10326         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10327         //if(this.wrap){
10328         //    this.wrap.addClass('x-item-disabled');
10329         //}
10330     },
10331
10332     // private
10333     onEnable : function(){
10334         this.inputEl().dom.disabled = false;
10335         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10336         //if(this.wrap){
10337         //    this.el.removeClass('x-item-disabled');
10338         //}
10339     },
10340
10341     // private
10342     onShow : function(){
10343         var ae = this.getActionEl();
10344         
10345         if(ae){
10346             ae.dom.style.display = '';
10347             ae.dom.style.visibility = 'visible';
10348         }
10349     },
10350
10351     // private
10352     
10353     onHide : function(){
10354         var ae = this.getActionEl();
10355         ae.dom.style.display = 'none';
10356     },
10357
10358     /**
10359      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10360      * by an implementing function.
10361      * @method
10362      * @param {EventObject} e
10363      */
10364     onTriggerClick : Roo.emptyFn
10365 });
10366  /*
10367  * Based on:
10368  * Ext JS Library 1.1.1
10369  * Copyright(c) 2006-2007, Ext JS, LLC.
10370  *
10371  * Originally Released Under LGPL - original licence link has changed is not relivant.
10372  *
10373  * Fork - LGPL
10374  * <script type="text/javascript">
10375  */
10376
10377
10378 /**
10379  * @class Roo.data.SortTypes
10380  * @singleton
10381  * Defines the default sorting (casting?) comparison functions used when sorting data.
10382  */
10383 Roo.data.SortTypes = {
10384     /**
10385      * Default sort that does nothing
10386      * @param {Mixed} s The value being converted
10387      * @return {Mixed} The comparison value
10388      */
10389     none : function(s){
10390         return s;
10391     },
10392     
10393     /**
10394      * The regular expression used to strip tags
10395      * @type {RegExp}
10396      * @property
10397      */
10398     stripTagsRE : /<\/?[^>]+>/gi,
10399     
10400     /**
10401      * Strips all HTML tags to sort on text only
10402      * @param {Mixed} s The value being converted
10403      * @return {String} The comparison value
10404      */
10405     asText : function(s){
10406         return String(s).replace(this.stripTagsRE, "");
10407     },
10408     
10409     /**
10410      * Strips all HTML tags to sort on text only - Case insensitive
10411      * @param {Mixed} s The value being converted
10412      * @return {String} The comparison value
10413      */
10414     asUCText : function(s){
10415         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10416     },
10417     
10418     /**
10419      * Case insensitive string
10420      * @param {Mixed} s The value being converted
10421      * @return {String} The comparison value
10422      */
10423     asUCString : function(s) {
10424         return String(s).toUpperCase();
10425     },
10426     
10427     /**
10428      * Date sorting
10429      * @param {Mixed} s The value being converted
10430      * @return {Number} The comparison value
10431      */
10432     asDate : function(s) {
10433         if(!s){
10434             return 0;
10435         }
10436         if(s instanceof Date){
10437             return s.getTime();
10438         }
10439         return Date.parse(String(s));
10440     },
10441     
10442     /**
10443      * Float sorting
10444      * @param {Mixed} s The value being converted
10445      * @return {Float} The comparison value
10446      */
10447     asFloat : function(s) {
10448         var val = parseFloat(String(s).replace(/,/g, ""));
10449         if(isNaN(val)) {
10450             val = 0;
10451         }
10452         return val;
10453     },
10454     
10455     /**
10456      * Integer sorting
10457      * @param {Mixed} s The value being converted
10458      * @return {Number} The comparison value
10459      */
10460     asInt : function(s) {
10461         var val = parseInt(String(s).replace(/,/g, ""));
10462         if(isNaN(val)) {
10463             val = 0;
10464         }
10465         return val;
10466     }
10467 };/*
10468  * Based on:
10469  * Ext JS Library 1.1.1
10470  * Copyright(c) 2006-2007, Ext JS, LLC.
10471  *
10472  * Originally Released Under LGPL - original licence link has changed is not relivant.
10473  *
10474  * Fork - LGPL
10475  * <script type="text/javascript">
10476  */
10477
10478 /**
10479 * @class Roo.data.Record
10480  * Instances of this class encapsulate both record <em>definition</em> information, and record
10481  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10482  * to access Records cached in an {@link Roo.data.Store} object.<br>
10483  * <p>
10484  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10485  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10486  * objects.<br>
10487  * <p>
10488  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10489  * @constructor
10490  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10491  * {@link #create}. The parameters are the same.
10492  * @param {Array} data An associative Array of data values keyed by the field name.
10493  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10494  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10495  * not specified an integer id is generated.
10496  */
10497 Roo.data.Record = function(data, id){
10498     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10499     this.data = data;
10500 };
10501
10502 /**
10503  * Generate a constructor for a specific record layout.
10504  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10505  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10506  * Each field definition object may contain the following properties: <ul>
10507  * <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,
10508  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10509  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10510  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10511  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10512  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10513  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10514  * this may be omitted.</p></li>
10515  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10516  * <ul><li>auto (Default, implies no conversion)</li>
10517  * <li>string</li>
10518  * <li>int</li>
10519  * <li>float</li>
10520  * <li>boolean</li>
10521  * <li>date</li></ul></p></li>
10522  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10523  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10524  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10525  * by the Reader into an object that will be stored in the Record. It is passed the
10526  * following parameters:<ul>
10527  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10528  * </ul></p></li>
10529  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10530  * </ul>
10531  * <br>usage:<br><pre><code>
10532 var TopicRecord = Roo.data.Record.create(
10533     {name: 'title', mapping: 'topic_title'},
10534     {name: 'author', mapping: 'username'},
10535     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10536     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10537     {name: 'lastPoster', mapping: 'user2'},
10538     {name: 'excerpt', mapping: 'post_text'}
10539 );
10540
10541 var myNewRecord = new TopicRecord({
10542     title: 'Do my job please',
10543     author: 'noobie',
10544     totalPosts: 1,
10545     lastPost: new Date(),
10546     lastPoster: 'Animal',
10547     excerpt: 'No way dude!'
10548 });
10549 myStore.add(myNewRecord);
10550 </code></pre>
10551  * @method create
10552  * @static
10553  */
10554 Roo.data.Record.create = function(o){
10555     var f = function(){
10556         f.superclass.constructor.apply(this, arguments);
10557     };
10558     Roo.extend(f, Roo.data.Record);
10559     var p = f.prototype;
10560     p.fields = new Roo.util.MixedCollection(false, function(field){
10561         return field.name;
10562     });
10563     for(var i = 0, len = o.length; i < len; i++){
10564         p.fields.add(new Roo.data.Field(o[i]));
10565     }
10566     f.getField = function(name){
10567         return p.fields.get(name);  
10568     };
10569     return f;
10570 };
10571
10572 Roo.data.Record.AUTO_ID = 1000;
10573 Roo.data.Record.EDIT = 'edit';
10574 Roo.data.Record.REJECT = 'reject';
10575 Roo.data.Record.COMMIT = 'commit';
10576
10577 Roo.data.Record.prototype = {
10578     /**
10579      * Readonly flag - true if this record has been modified.
10580      * @type Boolean
10581      */
10582     dirty : false,
10583     editing : false,
10584     error: null,
10585     modified: null,
10586
10587     // private
10588     join : function(store){
10589         this.store = store;
10590     },
10591
10592     /**
10593      * Set the named field to the specified value.
10594      * @param {String} name The name of the field to set.
10595      * @param {Object} value The value to set the field to.
10596      */
10597     set : function(name, value){
10598         if(this.data[name] == value){
10599             return;
10600         }
10601         this.dirty = true;
10602         if(!this.modified){
10603             this.modified = {};
10604         }
10605         if(typeof this.modified[name] == 'undefined'){
10606             this.modified[name] = this.data[name];
10607         }
10608         this.data[name] = value;
10609         if(!this.editing && this.store){
10610             this.store.afterEdit(this);
10611         }       
10612     },
10613
10614     /**
10615      * Get the value of the named field.
10616      * @param {String} name The name of the field to get the value of.
10617      * @return {Object} The value of the field.
10618      */
10619     get : function(name){
10620         return this.data[name]; 
10621     },
10622
10623     // private
10624     beginEdit : function(){
10625         this.editing = true;
10626         this.modified = {}; 
10627     },
10628
10629     // private
10630     cancelEdit : function(){
10631         this.editing = false;
10632         delete this.modified;
10633     },
10634
10635     // private
10636     endEdit : function(){
10637         this.editing = false;
10638         if(this.dirty && this.store){
10639             this.store.afterEdit(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Rejects all changes made to the Record since either creation, or the last commit operation.
10646      * Modified fields are reverted to their original values.
10647      * <p>
10648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10649      * of reject operations.
10650      */
10651     reject : function(){
10652         var m = this.modified;
10653         for(var n in m){
10654             if(typeof m[n] != "function"){
10655                 this.data[n] = m[n];
10656             }
10657         }
10658         this.dirty = false;
10659         delete this.modified;
10660         this.editing = false;
10661         if(this.store){
10662             this.store.afterReject(this);
10663         }
10664     },
10665
10666     /**
10667      * Usually called by the {@link Roo.data.Store} which owns the Record.
10668      * Commits all changes made to the Record since either creation, or the last commit operation.
10669      * <p>
10670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10671      * of commit operations.
10672      */
10673     commit : function(){
10674         this.dirty = false;
10675         delete this.modified;
10676         this.editing = false;
10677         if(this.store){
10678             this.store.afterCommit(this);
10679         }
10680     },
10681
10682     // private
10683     hasError : function(){
10684         return this.error != null;
10685     },
10686
10687     // private
10688     clearError : function(){
10689         this.error = null;
10690     },
10691
10692     /**
10693      * Creates a copy of this record.
10694      * @param {String} id (optional) A new record id if you don't want to use this record's id
10695      * @return {Record}
10696      */
10697     copy : function(newId) {
10698         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10699     }
10700 };/*
10701  * Based on:
10702  * Ext JS Library 1.1.1
10703  * Copyright(c) 2006-2007, Ext JS, LLC.
10704  *
10705  * Originally Released Under LGPL - original licence link has changed is not relivant.
10706  *
10707  * Fork - LGPL
10708  * <script type="text/javascript">
10709  */
10710
10711
10712
10713 /**
10714  * @class Roo.data.Store
10715  * @extends Roo.util.Observable
10716  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10717  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10718  * <p>
10719  * 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
10720  * has no knowledge of the format of the data returned by the Proxy.<br>
10721  * <p>
10722  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10723  * instances from the data object. These records are cached and made available through accessor functions.
10724  * @constructor
10725  * Creates a new Store.
10726  * @param {Object} config A config object containing the objects needed for the Store to access data,
10727  * and read the data into Records.
10728  */
10729 Roo.data.Store = function(config){
10730     this.data = new Roo.util.MixedCollection(false);
10731     this.data.getKey = function(o){
10732         return o.id;
10733     };
10734     this.baseParams = {};
10735     // private
10736     this.paramNames = {
10737         "start" : "start",
10738         "limit" : "limit",
10739         "sort" : "sort",
10740         "dir" : "dir",
10741         "multisort" : "_multisort"
10742     };
10743
10744     if(config && config.data){
10745         this.inlineData = config.data;
10746         delete config.data;
10747     }
10748
10749     Roo.apply(this, config);
10750     
10751     if(this.reader){ // reader passed
10752         this.reader = Roo.factory(this.reader, Roo.data);
10753         this.reader.xmodule = this.xmodule || false;
10754         if(!this.recordType){
10755             this.recordType = this.reader.recordType;
10756         }
10757         if(this.reader.onMetaChange){
10758             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10759         }
10760     }
10761
10762     if(this.recordType){
10763         this.fields = this.recordType.prototype.fields;
10764     }
10765     this.modified = [];
10766
10767     this.addEvents({
10768         /**
10769          * @event datachanged
10770          * Fires when the data cache has changed, and a widget which is using this Store
10771          * as a Record cache should refresh its view.
10772          * @param {Store} this
10773          */
10774         datachanged : true,
10775         /**
10776          * @event metachange
10777          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10778          * @param {Store} this
10779          * @param {Object} meta The JSON metadata
10780          */
10781         metachange : true,
10782         /**
10783          * @event add
10784          * Fires when Records have been added to the Store
10785          * @param {Store} this
10786          * @param {Roo.data.Record[]} records The array of Records added
10787          * @param {Number} index The index at which the record(s) were added
10788          */
10789         add : true,
10790         /**
10791          * @event remove
10792          * Fires when a Record has been removed from the Store
10793          * @param {Store} this
10794          * @param {Roo.data.Record} record The Record that was removed
10795          * @param {Number} index The index at which the record was removed
10796          */
10797         remove : true,
10798         /**
10799          * @event update
10800          * Fires when a Record has been updated
10801          * @param {Store} this
10802          * @param {Roo.data.Record} record The Record that was updated
10803          * @param {String} operation The update operation being performed.  Value may be one of:
10804          * <pre><code>
10805  Roo.data.Record.EDIT
10806  Roo.data.Record.REJECT
10807  Roo.data.Record.COMMIT
10808          * </code></pre>
10809          */
10810         update : true,
10811         /**
10812          * @event clear
10813          * Fires when the data cache has been cleared.
10814          * @param {Store} this
10815          */
10816         clear : true,
10817         /**
10818          * @event beforeload
10819          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10820          * the load action will be canceled.
10821          * @param {Store} this
10822          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10823          */
10824         beforeload : true,
10825         /**
10826          * @event beforeloadadd
10827          * Fires after a new set of Records has been loaded.
10828          * @param {Store} this
10829          * @param {Roo.data.Record[]} records The Records that were loaded
10830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10831          */
10832         beforeloadadd : true,
10833         /**
10834          * @event load
10835          * Fires after a new set of Records has been loaded, before they are added to the store.
10836          * @param {Store} this
10837          * @param {Roo.data.Record[]} records The Records that were loaded
10838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10839          * @params {Object} return from reader
10840          */
10841         load : true,
10842         /**
10843          * @event loadexception
10844          * Fires if an exception occurs in the Proxy during loading.
10845          * Called with the signature of the Proxy's "loadexception" event.
10846          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10847          * 
10848          * @param {Proxy} 
10849          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10850          * @param {Object} load options 
10851          * @param {Object} jsonData from your request (normally this contains the Exception)
10852          */
10853         loadexception : true
10854     });
10855     
10856     if(this.proxy){
10857         this.proxy = Roo.factory(this.proxy, Roo.data);
10858         this.proxy.xmodule = this.xmodule || false;
10859         this.relayEvents(this.proxy,  ["loadexception"]);
10860     }
10861     this.sortToggle = {};
10862     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10863
10864     Roo.data.Store.superclass.constructor.call(this);
10865
10866     if(this.inlineData){
10867         this.loadData(this.inlineData);
10868         delete this.inlineData;
10869     }
10870 };
10871
10872 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10873      /**
10874     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10875     * without a remote query - used by combo/forms at present.
10876     */
10877     
10878     /**
10879     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10880     */
10881     /**
10882     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10883     */
10884     /**
10885     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10886     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10887     */
10888     /**
10889     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10890     * on any HTTP request
10891     */
10892     /**
10893     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10894     */
10895     /**
10896     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10897     */
10898     multiSort: false,
10899     /**
10900     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10901     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10902     */
10903     remoteSort : false,
10904
10905     /**
10906     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10907      * loaded or when a record is removed. (defaults to false).
10908     */
10909     pruneModifiedRecords : false,
10910
10911     // private
10912     lastOptions : null,
10913
10914     /**
10915      * Add Records to the Store and fires the add event.
10916      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10917      */
10918     add : function(records){
10919         records = [].concat(records);
10920         for(var i = 0, len = records.length; i < len; i++){
10921             records[i].join(this);
10922         }
10923         var index = this.data.length;
10924         this.data.addAll(records);
10925         this.fireEvent("add", this, records, index);
10926     },
10927
10928     /**
10929      * Remove a Record from the Store and fires the remove event.
10930      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10931      */
10932     remove : function(record){
10933         var index = this.data.indexOf(record);
10934         this.data.removeAt(index);
10935         if(this.pruneModifiedRecords){
10936             this.modified.remove(record);
10937         }
10938         this.fireEvent("remove", this, record, index);
10939     },
10940
10941     /**
10942      * Remove all Records from the Store and fires the clear event.
10943      */
10944     removeAll : function(){
10945         this.data.clear();
10946         if(this.pruneModifiedRecords){
10947             this.modified = [];
10948         }
10949         this.fireEvent("clear", this);
10950     },
10951
10952     /**
10953      * Inserts Records to the Store at the given index and fires the add event.
10954      * @param {Number} index The start index at which to insert the passed Records.
10955      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10956      */
10957     insert : function(index, records){
10958         records = [].concat(records);
10959         for(var i = 0, len = records.length; i < len; i++){
10960             this.data.insert(index, records[i]);
10961             records[i].join(this);
10962         }
10963         this.fireEvent("add", this, records, index);
10964     },
10965
10966     /**
10967      * Get the index within the cache of the passed Record.
10968      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10969      * @return {Number} The index of the passed Record. Returns -1 if not found.
10970      */
10971     indexOf : function(record){
10972         return this.data.indexOf(record);
10973     },
10974
10975     /**
10976      * Get the index within the cache of the Record with the passed id.
10977      * @param {String} id The id of the Record to find.
10978      * @return {Number} The index of the Record. Returns -1 if not found.
10979      */
10980     indexOfId : function(id){
10981         return this.data.indexOfKey(id);
10982     },
10983
10984     /**
10985      * Get the Record with the specified id.
10986      * @param {String} id The id of the Record to find.
10987      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10988      */
10989     getById : function(id){
10990         return this.data.key(id);
10991     },
10992
10993     /**
10994      * Get the Record at the specified index.
10995      * @param {Number} index The index of the Record to find.
10996      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10997      */
10998     getAt : function(index){
10999         return this.data.itemAt(index);
11000     },
11001
11002     /**
11003      * Returns a range of Records between specified indices.
11004      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11005      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11006      * @return {Roo.data.Record[]} An array of Records
11007      */
11008     getRange : function(start, end){
11009         return this.data.getRange(start, end);
11010     },
11011
11012     // private
11013     storeOptions : function(o){
11014         o = Roo.apply({}, o);
11015         delete o.callback;
11016         delete o.scope;
11017         this.lastOptions = o;
11018     },
11019
11020     /**
11021      * Loads the Record cache from the configured Proxy using the configured Reader.
11022      * <p>
11023      * If using remote paging, then the first load call must specify the <em>start</em>
11024      * and <em>limit</em> properties in the options.params property to establish the initial
11025      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11026      * <p>
11027      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11028      * and this call will return before the new data has been loaded. Perform any post-processing
11029      * in a callback function, or in a "load" event handler.</strong>
11030      * <p>
11031      * @param {Object} options An object containing properties which control loading options:<ul>
11032      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11033      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11034      * passed the following arguments:<ul>
11035      * <li>r : Roo.data.Record[]</li>
11036      * <li>options: Options object from the load call</li>
11037      * <li>success: Boolean success indicator</li></ul></li>
11038      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11039      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11040      * </ul>
11041      */
11042     load : function(options){
11043         options = options || {};
11044         if(this.fireEvent("beforeload", this, options) !== false){
11045             this.storeOptions(options);
11046             var p = Roo.apply(options.params || {}, this.baseParams);
11047             // if meta was not loaded from remote source.. try requesting it.
11048             if (!this.reader.metaFromRemote) {
11049                 p._requestMeta = 1;
11050             }
11051             if(this.sortInfo && this.remoteSort){
11052                 var pn = this.paramNames;
11053                 p[pn["sort"]] = this.sortInfo.field;
11054                 p[pn["dir"]] = this.sortInfo.direction;
11055             }
11056             if (this.multiSort) {
11057                 var pn = this.paramNames;
11058                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11059             }
11060             
11061             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11062         }
11063     },
11064
11065     /**
11066      * Reloads the Record cache from the configured Proxy using the configured Reader and
11067      * the options from the last load operation performed.
11068      * @param {Object} options (optional) An object containing properties which may override the options
11069      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11070      * the most recently used options are reused).
11071      */
11072     reload : function(options){
11073         this.load(Roo.applyIf(options||{}, this.lastOptions));
11074     },
11075
11076     // private
11077     // Called as a callback by the Reader during a load operation.
11078     loadRecords : function(o, options, success){
11079         if(!o || success === false){
11080             if(success !== false){
11081                 this.fireEvent("load", this, [], options, o);
11082             }
11083             if(options.callback){
11084                 options.callback.call(options.scope || this, [], options, false);
11085             }
11086             return;
11087         }
11088         // if data returned failure - throw an exception.
11089         if (o.success === false) {
11090             // show a message if no listener is registered.
11091             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11092                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11093             }
11094             // loadmask wil be hooked into this..
11095             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11096             return;
11097         }
11098         var r = o.records, t = o.totalRecords || r.length;
11099         
11100         this.fireEvent("beforeloadadd", this, r, options, o);
11101         
11102         if(!options || options.add !== true){
11103             if(this.pruneModifiedRecords){
11104                 this.modified = [];
11105             }
11106             for(var i = 0, len = r.length; i < len; i++){
11107                 r[i].join(this);
11108             }
11109             if(this.snapshot){
11110                 this.data = this.snapshot;
11111                 delete this.snapshot;
11112             }
11113             this.data.clear();
11114             this.data.addAll(r);
11115             this.totalLength = t;
11116             this.applySort();
11117             this.fireEvent("datachanged", this);
11118         }else{
11119             this.totalLength = Math.max(t, this.data.length+r.length);
11120             this.add(r);
11121         }
11122         
11123         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11124                 
11125             var e = new Roo.data.Record({});
11126
11127             e.set(this.parent.displayField, this.parent.emptyTitle);
11128             e.set(this.parent.valueField, '');
11129
11130             this.insert(0, e);
11131         }
11132             
11133         this.fireEvent("load", this, r, options, o);
11134         if(options.callback){
11135             options.callback.call(options.scope || this, r, options, true);
11136         }
11137     },
11138
11139
11140     /**
11141      * Loads data from a passed data block. A Reader which understands the format of the data
11142      * must have been configured in the constructor.
11143      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11144      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11145      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11146      */
11147     loadData : function(o, append){
11148         var r = this.reader.readRecords(o);
11149         this.loadRecords(r, {add: append}, true);
11150     },
11151
11152     /**
11153      * Gets the number of cached records.
11154      * <p>
11155      * <em>If using paging, this may not be the total size of the dataset. If the data object
11156      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11157      * the data set size</em>
11158      */
11159     getCount : function(){
11160         return this.data.length || 0;
11161     },
11162
11163     /**
11164      * Gets the total number of records in the dataset as returned by the server.
11165      * <p>
11166      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11167      * the dataset size</em>
11168      */
11169     getTotalCount : function(){
11170         return this.totalLength || 0;
11171     },
11172
11173     /**
11174      * Returns the sort state of the Store as an object with two properties:
11175      * <pre><code>
11176  field {String} The name of the field by which the Records are sorted
11177  direction {String} The sort order, "ASC" or "DESC"
11178      * </code></pre>
11179      */
11180     getSortState : function(){
11181         return this.sortInfo;
11182     },
11183
11184     // private
11185     applySort : function(){
11186         if(this.sortInfo && !this.remoteSort){
11187             var s = this.sortInfo, f = s.field;
11188             var st = this.fields.get(f).sortType;
11189             var fn = function(r1, r2){
11190                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11191                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11192             };
11193             this.data.sort(s.direction, fn);
11194             if(this.snapshot && this.snapshot != this.data){
11195                 this.snapshot.sort(s.direction, fn);
11196             }
11197         }
11198     },
11199
11200     /**
11201      * Sets the default sort column and order to be used by the next load operation.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     setDefaultSort : function(field, dir){
11206         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11207     },
11208
11209     /**
11210      * Sort the Records.
11211      * If remote sorting is used, the sort is performed on the server, and the cache is
11212      * reloaded. If local sorting is used, the cache is sorted internally.
11213      * @param {String} fieldName The name of the field to sort by.
11214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11215      */
11216     sort : function(fieldName, dir){
11217         var f = this.fields.get(fieldName);
11218         if(!dir){
11219             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11220             
11221             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11222                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11223             }else{
11224                 dir = f.sortDir;
11225             }
11226         }
11227         this.sortToggle[f.name] = dir;
11228         this.sortInfo = {field: f.name, direction: dir};
11229         if(!this.remoteSort){
11230             this.applySort();
11231             this.fireEvent("datachanged", this);
11232         }else{
11233             this.load(this.lastOptions);
11234         }
11235     },
11236
11237     /**
11238      * Calls the specified function for each of the Records in the cache.
11239      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11240      * Returning <em>false</em> aborts and exits the iteration.
11241      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11242      */
11243     each : function(fn, scope){
11244         this.data.each(fn, scope);
11245     },
11246
11247     /**
11248      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11249      * (e.g., during paging).
11250      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11251      */
11252     getModifiedRecords : function(){
11253         return this.modified;
11254     },
11255
11256     // private
11257     createFilterFn : function(property, value, anyMatch){
11258         if(!value.exec){ // not a regex
11259             value = String(value);
11260             if(value.length == 0){
11261                 return false;
11262             }
11263             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11264         }
11265         return function(r){
11266             return value.test(r.data[property]);
11267         };
11268     },
11269
11270     /**
11271      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11272      * @param {String} property A field on your records
11273      * @param {Number} start The record index to start at (defaults to 0)
11274      * @param {Number} end The last record index to include (defaults to length - 1)
11275      * @return {Number} The sum
11276      */
11277     sum : function(property, start, end){
11278         var rs = this.data.items, v = 0;
11279         start = start || 0;
11280         end = (end || end === 0) ? end : rs.length-1;
11281
11282         for(var i = start; i <= end; i++){
11283             v += (rs[i].data[property] || 0);
11284         }
11285         return v;
11286     },
11287
11288     /**
11289      * Filter the records by a specified property.
11290      * @param {String} field A field on your records
11291      * @param {String/RegExp} value Either a string that the field
11292      * should start with or a RegExp to test against the field
11293      * @param {Boolean} anyMatch True to match any part not just the beginning
11294      */
11295     filter : function(property, value, anyMatch){
11296         var fn = this.createFilterFn(property, value, anyMatch);
11297         return fn ? this.filterBy(fn) : this.clearFilter();
11298     },
11299
11300     /**
11301      * Filter by a function. The specified function will be called with each
11302      * record in this data source. If the function returns true the record is included,
11303      * otherwise it is filtered.
11304      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11305      * @param {Object} scope (optional) The scope of the function (defaults to this)
11306      */
11307     filterBy : function(fn, scope){
11308         this.snapshot = this.snapshot || this.data;
11309         this.data = this.queryBy(fn, scope||this);
11310         this.fireEvent("datachanged", this);
11311     },
11312
11313     /**
11314      * Query the records by a specified property.
11315      * @param {String} field A field on your records
11316      * @param {String/RegExp} value Either a string that the field
11317      * should start with or a RegExp to test against the field
11318      * @param {Boolean} anyMatch True to match any part not just the beginning
11319      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11320      */
11321     query : function(property, value, anyMatch){
11322         var fn = this.createFilterFn(property, value, anyMatch);
11323         return fn ? this.queryBy(fn) : this.data.clone();
11324     },
11325
11326     /**
11327      * Query by a function. The specified function will be called with each
11328      * record in this data source. If the function returns true the record is included
11329      * in the results.
11330      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11331      * @param {Object} scope (optional) The scope of the function (defaults to this)
11332       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11333      **/
11334     queryBy : function(fn, scope){
11335         var data = this.snapshot || this.data;
11336         return data.filterBy(fn, scope||this);
11337     },
11338
11339     /**
11340      * Collects unique values for a particular dataIndex from this store.
11341      * @param {String} dataIndex The property to collect
11342      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11343      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11344      * @return {Array} An array of the unique values
11345      **/
11346     collect : function(dataIndex, allowNull, bypassFilter){
11347         var d = (bypassFilter === true && this.snapshot) ?
11348                 this.snapshot.items : this.data.items;
11349         var v, sv, r = [], l = {};
11350         for(var i = 0, len = d.length; i < len; i++){
11351             v = d[i].data[dataIndex];
11352             sv = String(v);
11353             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11354                 l[sv] = true;
11355                 r[r.length] = v;
11356             }
11357         }
11358         return r;
11359     },
11360
11361     /**
11362      * Revert to a view of the Record cache with no filtering applied.
11363      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11364      */
11365     clearFilter : function(suppressEvent){
11366         if(this.snapshot && this.snapshot != this.data){
11367             this.data = this.snapshot;
11368             delete this.snapshot;
11369             if(suppressEvent !== true){
11370                 this.fireEvent("datachanged", this);
11371             }
11372         }
11373     },
11374
11375     // private
11376     afterEdit : function(record){
11377         if(this.modified.indexOf(record) == -1){
11378             this.modified.push(record);
11379         }
11380         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11381     },
11382     
11383     // private
11384     afterReject : function(record){
11385         this.modified.remove(record);
11386         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11387     },
11388
11389     // private
11390     afterCommit : function(record){
11391         this.modified.remove(record);
11392         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11393     },
11394
11395     /**
11396      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11397      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11398      */
11399     commitChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].commit();
11404         }
11405     },
11406
11407     /**
11408      * Cancel outstanding changes on all changed records.
11409      */
11410     rejectChanges : function(){
11411         var m = this.modified.slice(0);
11412         this.modified = [];
11413         for(var i = 0, len = m.length; i < len; i++){
11414             m[i].reject();
11415         }
11416     },
11417
11418     onMetaChange : function(meta, rtype, o){
11419         this.recordType = rtype;
11420         this.fields = rtype.prototype.fields;
11421         delete this.snapshot;
11422         this.sortInfo = meta.sortInfo || this.sortInfo;
11423         this.modified = [];
11424         this.fireEvent('metachange', this, this.reader.meta);
11425     },
11426     
11427     moveIndex : function(data, type)
11428     {
11429         var index = this.indexOf(data);
11430         
11431         var newIndex = index + type;
11432         
11433         this.remove(data);
11434         
11435         this.insert(newIndex, data);
11436         
11437     }
11438 });/*
11439  * Based on:
11440  * Ext JS Library 1.1.1
11441  * Copyright(c) 2006-2007, Ext JS, LLC.
11442  *
11443  * Originally Released Under LGPL - original licence link has changed is not relivant.
11444  *
11445  * Fork - LGPL
11446  * <script type="text/javascript">
11447  */
11448
11449 /**
11450  * @class Roo.data.SimpleStore
11451  * @extends Roo.data.Store
11452  * Small helper class to make creating Stores from Array data easier.
11453  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11454  * @cfg {Array} fields An array of field definition objects, or field name strings.
11455  * @cfg {Array} data The multi-dimensional array of data
11456  * @constructor
11457  * @param {Object} config
11458  */
11459 Roo.data.SimpleStore = function(config){
11460     Roo.data.SimpleStore.superclass.constructor.call(this, {
11461         isLocal : true,
11462         reader: new Roo.data.ArrayReader({
11463                 id: config.id
11464             },
11465             Roo.data.Record.create(config.fields)
11466         ),
11467         proxy : new Roo.data.MemoryProxy(config.data)
11468     });
11469     this.load();
11470 };
11471 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11472  * Based on:
11473  * Ext JS Library 1.1.1
11474  * Copyright(c) 2006-2007, Ext JS, LLC.
11475  *
11476  * Originally Released Under LGPL - original licence link has changed is not relivant.
11477  *
11478  * Fork - LGPL
11479  * <script type="text/javascript">
11480  */
11481
11482 /**
11483 /**
11484  * @extends Roo.data.Store
11485  * @class Roo.data.JsonStore
11486  * Small helper class to make creating Stores for JSON data easier. <br/>
11487 <pre><code>
11488 var store = new Roo.data.JsonStore({
11489     url: 'get-images.php',
11490     root: 'images',
11491     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11492 });
11493 </code></pre>
11494  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11495  * JsonReader and HttpProxy (unless inline data is provided).</b>
11496  * @cfg {Array} fields An array of field definition objects, or field name strings.
11497  * @constructor
11498  * @param {Object} config
11499  */
11500 Roo.data.JsonStore = function(c){
11501     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11502         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11503         reader: new Roo.data.JsonReader(c, c.fields)
11504     }));
11505 };
11506 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11507  * Based on:
11508  * Ext JS Library 1.1.1
11509  * Copyright(c) 2006-2007, Ext JS, LLC.
11510  *
11511  * Originally Released Under LGPL - original licence link has changed is not relivant.
11512  *
11513  * Fork - LGPL
11514  * <script type="text/javascript">
11515  */
11516
11517  
11518 Roo.data.Field = function(config){
11519     if(typeof config == "string"){
11520         config = {name: config};
11521     }
11522     Roo.apply(this, config);
11523     
11524     if(!this.type){
11525         this.type = "auto";
11526     }
11527     
11528     var st = Roo.data.SortTypes;
11529     // named sortTypes are supported, here we look them up
11530     if(typeof this.sortType == "string"){
11531         this.sortType = st[this.sortType];
11532     }
11533     
11534     // set default sortType for strings and dates
11535     if(!this.sortType){
11536         switch(this.type){
11537             case "string":
11538                 this.sortType = st.asUCString;
11539                 break;
11540             case "date":
11541                 this.sortType = st.asDate;
11542                 break;
11543             default:
11544                 this.sortType = st.none;
11545         }
11546     }
11547
11548     // define once
11549     var stripRe = /[\$,%]/g;
11550
11551     // prebuilt conversion function for this field, instead of
11552     // switching every time we're reading a value
11553     if(!this.convert){
11554         var cv, dateFormat = this.dateFormat;
11555         switch(this.type){
11556             case "":
11557             case "auto":
11558             case undefined:
11559                 cv = function(v){ return v; };
11560                 break;
11561             case "string":
11562                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11563                 break;
11564             case "int":
11565                 cv = function(v){
11566                     return v !== undefined && v !== null && v !== '' ?
11567                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11568                     };
11569                 break;
11570             case "float":
11571                 cv = function(v){
11572                     return v !== undefined && v !== null && v !== '' ?
11573                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11574                     };
11575                 break;
11576             case "bool":
11577             case "boolean":
11578                 cv = function(v){ return v === true || v === "true" || v == 1; };
11579                 break;
11580             case "date":
11581                 cv = function(v){
11582                     if(!v){
11583                         return '';
11584                     }
11585                     if(v instanceof Date){
11586                         return v;
11587                     }
11588                     if(dateFormat){
11589                         if(dateFormat == "timestamp"){
11590                             return new Date(v*1000);
11591                         }
11592                         return Date.parseDate(v, dateFormat);
11593                     }
11594                     var parsed = Date.parse(v);
11595                     return parsed ? new Date(parsed) : null;
11596                 };
11597              break;
11598             
11599         }
11600         this.convert = cv;
11601     }
11602 };
11603
11604 Roo.data.Field.prototype = {
11605     dateFormat: null,
11606     defaultValue: "",
11607     mapping: null,
11608     sortType : null,
11609     sortDir : "ASC"
11610 };/*
11611  * Based on:
11612  * Ext JS Library 1.1.1
11613  * Copyright(c) 2006-2007, Ext JS, LLC.
11614  *
11615  * Originally Released Under LGPL - original licence link has changed is not relivant.
11616  *
11617  * Fork - LGPL
11618  * <script type="text/javascript">
11619  */
11620  
11621 // Base class for reading structured data from a data source.  This class is intended to be
11622 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11623
11624 /**
11625  * @class Roo.data.DataReader
11626  * Base class for reading structured data from a data source.  This class is intended to be
11627  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11628  */
11629
11630 Roo.data.DataReader = function(meta, recordType){
11631     
11632     this.meta = meta;
11633     
11634     this.recordType = recordType instanceof Array ? 
11635         Roo.data.Record.create(recordType) : recordType;
11636 };
11637
11638 Roo.data.DataReader.prototype = {
11639      /**
11640      * Create an empty record
11641      * @param {Object} data (optional) - overlay some values
11642      * @return {Roo.data.Record} record created.
11643      */
11644     newRow :  function(d) {
11645         var da =  {};
11646         this.recordType.prototype.fields.each(function(c) {
11647             switch( c.type) {
11648                 case 'int' : da[c.name] = 0; break;
11649                 case 'date' : da[c.name] = new Date(); break;
11650                 case 'float' : da[c.name] = 0.0; break;
11651                 case 'boolean' : da[c.name] = false; break;
11652                 default : da[c.name] = ""; break;
11653             }
11654             
11655         });
11656         return new this.recordType(Roo.apply(da, d));
11657     }
11658     
11659 };/*
11660  * Based on:
11661  * Ext JS Library 1.1.1
11662  * Copyright(c) 2006-2007, Ext JS, LLC.
11663  *
11664  * Originally Released Under LGPL - original licence link has changed is not relivant.
11665  *
11666  * Fork - LGPL
11667  * <script type="text/javascript">
11668  */
11669
11670 /**
11671  * @class Roo.data.DataProxy
11672  * @extends Roo.data.Observable
11673  * This class is an abstract base class for implementations which provide retrieval of
11674  * unformatted data objects.<br>
11675  * <p>
11676  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11677  * (of the appropriate type which knows how to parse the data object) to provide a block of
11678  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11679  * <p>
11680  * Custom implementations must implement the load method as described in
11681  * {@link Roo.data.HttpProxy#load}.
11682  */
11683 Roo.data.DataProxy = function(){
11684     this.addEvents({
11685         /**
11686          * @event beforeload
11687          * Fires before a network request is made to retrieve a data object.
11688          * @param {Object} This DataProxy object.
11689          * @param {Object} params The params parameter to the load function.
11690          */
11691         beforeload : true,
11692         /**
11693          * @event load
11694          * Fires before the load method's callback is called.
11695          * @param {Object} This DataProxy object.
11696          * @param {Object} o The data object.
11697          * @param {Object} arg The callback argument object passed to the load function.
11698          */
11699         load : true,
11700         /**
11701          * @event loadexception
11702          * Fires if an Exception occurs during data retrieval.
11703          * @param {Object} This DataProxy object.
11704          * @param {Object} o The data object.
11705          * @param {Object} arg The callback argument object passed to the load function.
11706          * @param {Object} e The Exception.
11707          */
11708         loadexception : true
11709     });
11710     Roo.data.DataProxy.superclass.constructor.call(this);
11711 };
11712
11713 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11714
11715     /**
11716      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11717      */
11718 /*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728 /**
11729  * @class Roo.data.MemoryProxy
11730  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11731  * to the Reader when its load method is called.
11732  * @constructor
11733  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11734  */
11735 Roo.data.MemoryProxy = function(data){
11736     if (data.data) {
11737         data = data.data;
11738     }
11739     Roo.data.MemoryProxy.superclass.constructor.call(this);
11740     this.data = data;
11741 };
11742
11743 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11744     
11745     /**
11746      * Load data from the requested source (in this case an in-memory
11747      * data object passed to the constructor), read the data object into
11748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11749      * process that block using the passed callback.
11750      * @param {Object} params This parameter is not used by the MemoryProxy class.
11751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11752      * object into a block of Roo.data.Records.
11753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11754      * The function must be passed <ul>
11755      * <li>The Record block object</li>
11756      * <li>The "arg" argument from the load function</li>
11757      * <li>A boolean success indicator</li>
11758      * </ul>
11759      * @param {Object} scope The scope in which to call the callback
11760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11761      */
11762     load : function(params, reader, callback, scope, arg){
11763         params = params || {};
11764         var result;
11765         try {
11766             result = reader.readRecords(this.data);
11767         }catch(e){
11768             this.fireEvent("loadexception", this, arg, null, e);
11769             callback.call(scope, null, arg, false);
11770             return;
11771         }
11772         callback.call(scope, result, arg, true);
11773     },
11774     
11775     // private
11776     update : function(params, records){
11777         
11778     }
11779 });/*
11780  * Based on:
11781  * Ext JS Library 1.1.1
11782  * Copyright(c) 2006-2007, Ext JS, LLC.
11783  *
11784  * Originally Released Under LGPL - original licence link has changed is not relivant.
11785  *
11786  * Fork - LGPL
11787  * <script type="text/javascript">
11788  */
11789 /**
11790  * @class Roo.data.HttpProxy
11791  * @extends Roo.data.DataProxy
11792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11793  * configured to reference a certain URL.<br><br>
11794  * <p>
11795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11796  * from which the running page was served.<br><br>
11797  * <p>
11798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11799  * <p>
11800  * Be aware that to enable the browser to parse an XML document, the server must set
11801  * the Content-Type header in the HTTP response to "text/xml".
11802  * @constructor
11803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11805  * will be used to make the request.
11806  */
11807 Roo.data.HttpProxy = function(conn){
11808     Roo.data.HttpProxy.superclass.constructor.call(this);
11809     // is conn a conn config or a real conn?
11810     this.conn = conn;
11811     this.useAjax = !conn || !conn.events;
11812   
11813 };
11814
11815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11816     // thse are take from connection...
11817     
11818     /**
11819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11820      */
11821     /**
11822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11823      * extra parameters to each request made by this object. (defaults to undefined)
11824      */
11825     /**
11826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11827      *  to each request made by this object. (defaults to undefined)
11828      */
11829     /**
11830      * @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)
11831      */
11832     /**
11833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11834      */
11835      /**
11836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11837      * @type Boolean
11838      */
11839   
11840
11841     /**
11842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11843      * @type Boolean
11844      */
11845     /**
11846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11848      * a finer-grained basis than the DataProxy events.
11849      */
11850     getConnection : function(){
11851         return this.useAjax ? Roo.Ajax : this.conn;
11852     },
11853
11854     /**
11855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11857      * process that block using the passed callback.
11858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11859      * for the request to the remote server.
11860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11861      * object into a block of Roo.data.Records.
11862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11863      * The function must be passed <ul>
11864      * <li>The Record block object</li>
11865      * <li>The "arg" argument from the load function</li>
11866      * <li>A boolean success indicator</li>
11867      * </ul>
11868      * @param {Object} scope The scope in which to call the callback
11869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11870      */
11871     load : function(params, reader, callback, scope, arg){
11872         if(this.fireEvent("beforeload", this, params) !== false){
11873             var  o = {
11874                 params : params || {},
11875                 request: {
11876                     callback : callback,
11877                     scope : scope,
11878                     arg : arg
11879                 },
11880                 reader: reader,
11881                 callback : this.loadResponse,
11882                 scope: this
11883             };
11884             if(this.useAjax){
11885                 Roo.applyIf(o, this.conn);
11886                 if(this.activeRequest){
11887                     Roo.Ajax.abort(this.activeRequest);
11888                 }
11889                 this.activeRequest = Roo.Ajax.request(o);
11890             }else{
11891                 this.conn.request(o);
11892             }
11893         }else{
11894             callback.call(scope||this, null, arg, false);
11895         }
11896     },
11897
11898     // private
11899     loadResponse : function(o, success, response){
11900         delete this.activeRequest;
11901         if(!success){
11902             this.fireEvent("loadexception", this, o, response);
11903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11904             return;
11905         }
11906         var result;
11907         try {
11908             result = o.reader.read(response);
11909         }catch(e){
11910             this.fireEvent("loadexception", this, o, response, e);
11911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11912             return;
11913         }
11914         
11915         this.fireEvent("load", this, o, o.request.arg);
11916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11917     },
11918
11919     // private
11920     update : function(dataSet){
11921
11922     },
11923
11924     // private
11925     updateResponse : function(dataSet){
11926
11927     }
11928 });/*
11929  * Based on:
11930  * Ext JS Library 1.1.1
11931  * Copyright(c) 2006-2007, Ext JS, LLC.
11932  *
11933  * Originally Released Under LGPL - original licence link has changed is not relivant.
11934  *
11935  * Fork - LGPL
11936  * <script type="text/javascript">
11937  */
11938
11939 /**
11940  * @class Roo.data.ScriptTagProxy
11941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11942  * other than the originating domain of the running page.<br><br>
11943  * <p>
11944  * <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
11945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11946  * <p>
11947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11948  * source code that is used as the source inside a &lt;script> tag.<br><br>
11949  * <p>
11950  * In order for the browser to process the returned data, the server must wrap the data object
11951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11953  * depending on whether the callback name was passed:
11954  * <p>
11955  * <pre><code>
11956 boolean scriptTag = false;
11957 String cb = request.getParameter("callback");
11958 if (cb != null) {
11959     scriptTag = true;
11960     response.setContentType("text/javascript");
11961 } else {
11962     response.setContentType("application/x-json");
11963 }
11964 Writer out = response.getWriter();
11965 if (scriptTag) {
11966     out.write(cb + "(");
11967 }
11968 out.print(dataBlock.toJsonString());
11969 if (scriptTag) {
11970     out.write(");");
11971 }
11972 </pre></code>
11973  *
11974  * @constructor
11975  * @param {Object} config A configuration object.
11976  */
11977 Roo.data.ScriptTagProxy = function(config){
11978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11979     Roo.apply(this, config);
11980     this.head = document.getElementsByTagName("head")[0];
11981 };
11982
11983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11984
11985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11986     /**
11987      * @cfg {String} url The URL from which to request the data object.
11988      */
11989     /**
11990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11991      */
11992     timeout : 30000,
11993     /**
11994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11995      * the server the name of the callback function set up by the load call to process the returned data object.
11996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11997      * javascript output which calls this named function passing the data object as its only parameter.
11998      */
11999     callbackParam : "callback",
12000     /**
12001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12002      * name to the request.
12003      */
12004     nocache : true,
12005
12006     /**
12007      * Load data from the configured URL, read the data object into
12008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009      * process that block using the passed callback.
12010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12011      * for the request to the remote server.
12012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013      * object into a block of Roo.data.Records.
12014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12015      * The function must be passed <ul>
12016      * <li>The Record block object</li>
12017      * <li>The "arg" argument from the load function</li>
12018      * <li>A boolean success indicator</li>
12019      * </ul>
12020      * @param {Object} scope The scope in which to call the callback
12021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022      */
12023     load : function(params, reader, callback, scope, arg){
12024         if(this.fireEvent("beforeload", this, params) !== false){
12025
12026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12027
12028             var url = this.url;
12029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12030             if(this.nocache){
12031                 url += "&_dc=" + (new Date().getTime());
12032             }
12033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12034             var trans = {
12035                 id : transId,
12036                 cb : "stcCallback"+transId,
12037                 scriptId : "stcScript"+transId,
12038                 params : params,
12039                 arg : arg,
12040                 url : url,
12041                 callback : callback,
12042                 scope : scope,
12043                 reader : reader
12044             };
12045             var conn = this;
12046
12047             window[trans.cb] = function(o){
12048                 conn.handleResponse(o, trans);
12049             };
12050
12051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12052
12053             if(this.autoAbort !== false){
12054                 this.abort();
12055             }
12056
12057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12058
12059             var script = document.createElement("script");
12060             script.setAttribute("src", url);
12061             script.setAttribute("type", "text/javascript");
12062             script.setAttribute("id", trans.scriptId);
12063             this.head.appendChild(script);
12064
12065             this.trans = trans;
12066         }else{
12067             callback.call(scope||this, null, arg, false);
12068         }
12069     },
12070
12071     // private
12072     isLoading : function(){
12073         return this.trans ? true : false;
12074     },
12075
12076     /**
12077      * Abort the current server request.
12078      */
12079     abort : function(){
12080         if(this.isLoading()){
12081             this.destroyTrans(this.trans);
12082         }
12083     },
12084
12085     // private
12086     destroyTrans : function(trans, isLoaded){
12087         this.head.removeChild(document.getElementById(trans.scriptId));
12088         clearTimeout(trans.timeoutId);
12089         if(isLoaded){
12090             window[trans.cb] = undefined;
12091             try{
12092                 delete window[trans.cb];
12093             }catch(e){}
12094         }else{
12095             // if hasn't been loaded, wait for load to remove it to prevent script error
12096             window[trans.cb] = function(){
12097                 window[trans.cb] = undefined;
12098                 try{
12099                     delete window[trans.cb];
12100                 }catch(e){}
12101             };
12102         }
12103     },
12104
12105     // private
12106     handleResponse : function(o, trans){
12107         this.trans = false;
12108         this.destroyTrans(trans, true);
12109         var result;
12110         try {
12111             result = trans.reader.readRecords(o);
12112         }catch(e){
12113             this.fireEvent("loadexception", this, o, trans.arg, e);
12114             trans.callback.call(trans.scope||window, null, trans.arg, false);
12115             return;
12116         }
12117         this.fireEvent("load", this, o, trans.arg);
12118         trans.callback.call(trans.scope||window, result, trans.arg, true);
12119     },
12120
12121     // private
12122     handleFailure : function(trans){
12123         this.trans = false;
12124         this.destroyTrans(trans, false);
12125         this.fireEvent("loadexception", this, null, trans.arg);
12126         trans.callback.call(trans.scope||window, null, trans.arg, false);
12127     }
12128 });/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139 /**
12140  * @class Roo.data.JsonReader
12141  * @extends Roo.data.DataReader
12142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12143  * based on mappings in a provided Roo.data.Record constructor.
12144  * 
12145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12146  * in the reply previously. 
12147  * 
12148  * <p>
12149  * Example code:
12150  * <pre><code>
12151 var RecordDef = Roo.data.Record.create([
12152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12154 ]);
12155 var myReader = new Roo.data.JsonReader({
12156     totalProperty: "results",    // The property which contains the total dataset size (optional)
12157     root: "rows",                // The property which contains an Array of row objects
12158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12159 }, RecordDef);
12160 </code></pre>
12161  * <p>
12162  * This would consume a JSON file like this:
12163  * <pre><code>
12164 { 'results': 2, 'rows': [
12165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12167 }
12168 </code></pre>
12169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12171  * paged from the remote server.
12172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12173  * @cfg {String} root name of the property which contains the Array of row objects.
12174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12175  * @cfg {Array} fields Array of field definition objects
12176  * @constructor
12177  * Create a new JsonReader
12178  * @param {Object} meta Metadata configuration options
12179  * @param {Object} recordType Either an Array of field definition objects,
12180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12181  */
12182 Roo.data.JsonReader = function(meta, recordType){
12183     
12184     meta = meta || {};
12185     // set some defaults:
12186     Roo.applyIf(meta, {
12187         totalProperty: 'total',
12188         successProperty : 'success',
12189         root : 'data',
12190         id : 'id'
12191     });
12192     
12193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12194 };
12195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12196     
12197     /**
12198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12199      * Used by Store query builder to append _requestMeta to params.
12200      * 
12201      */
12202     metaFromRemote : false,
12203     /**
12204      * This method is only used by a DataProxy which has retrieved data from a remote server.
12205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12206      * @return {Object} data A data block which is used by an Roo.data.Store object as
12207      * a cache of Roo.data.Records.
12208      */
12209     read : function(response){
12210         var json = response.responseText;
12211        
12212         var o = /* eval:var:o */ eval("("+json+")");
12213         if(!o) {
12214             throw {message: "JsonReader.read: Json object not found"};
12215         }
12216         
12217         if(o.metaData){
12218             
12219             delete this.ef;
12220             this.metaFromRemote = true;
12221             this.meta = o.metaData;
12222             this.recordType = Roo.data.Record.create(o.metaData.fields);
12223             this.onMetaChange(this.meta, this.recordType, o);
12224         }
12225         return this.readRecords(o);
12226     },
12227
12228     // private function a store will implement
12229     onMetaChange : function(meta, recordType, o){
12230
12231     },
12232
12233     /**
12234          * @ignore
12235          */
12236     simpleAccess: function(obj, subsc) {
12237         return obj[subsc];
12238     },
12239
12240         /**
12241          * @ignore
12242          */
12243     getJsonAccessor: function(){
12244         var re = /[\[\.]/;
12245         return function(expr) {
12246             try {
12247                 return(re.test(expr))
12248                     ? new Function("obj", "return obj." + expr)
12249                     : function(obj){
12250                         return obj[expr];
12251                     };
12252             } catch(e){}
12253             return Roo.emptyFn;
12254         };
12255     }(),
12256
12257     /**
12258      * Create a data block containing Roo.data.Records from an XML document.
12259      * @param {Object} o An object which contains an Array of row objects in the property specified
12260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12261      * which contains the total size of the dataset.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     readRecords : function(o){
12266         /**
12267          * After any data loads, the raw JSON data is available for further custom processing.
12268          * @type Object
12269          */
12270         this.o = o;
12271         var s = this.meta, Record = this.recordType,
12272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12273
12274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12275         if (!this.ef) {
12276             if(s.totalProperty) {
12277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12278                 }
12279                 if(s.successProperty) {
12280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12281                 }
12282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12283                 if (s.id) {
12284                         var g = this.getJsonAccessor(s.id);
12285                         this.getId = function(rec) {
12286                                 var r = g(rec);  
12287                                 return (r === undefined || r === "") ? null : r;
12288                         };
12289                 } else {
12290                         this.getId = function(){return null;};
12291                 }
12292             this.ef = [];
12293             for(var jj = 0; jj < fl; jj++){
12294                 f = fi[jj];
12295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12296                 this.ef[jj] = this.getJsonAccessor(map);
12297             }
12298         }
12299
12300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12301         if(s.totalProperty){
12302             var vt = parseInt(this.getTotal(o), 10);
12303             if(!isNaN(vt)){
12304                 totalRecords = vt;
12305             }
12306         }
12307         if(s.successProperty){
12308             var vs = this.getSuccess(o);
12309             if(vs === false || vs === 'false'){
12310                 success = false;
12311             }
12312         }
12313         
12314         
12315         Roo.log('--------------------------');
12316         Roo.log(root);
12317         
12318         var records = [];
12319         for(var i = 0; i < c; i++){
12320                 var n = root[i];
12321             var values = {};
12322             var id = this.getId(n);
12323             for(var j = 0; j < fl; j++){
12324                 f = fi[j];
12325             var v = this.ef[j](n);
12326             if (!f.convert) {
12327                 Roo.log('missing convert for ' + f.name);
12328                 Roo.log(f);
12329                 continue;
12330             }
12331             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12332             }
12333             var record = new Record(values, id);
12334             record.json = n;
12335             records[i] = record;
12336         }
12337         return {
12338             raw : o,
12339             success : success,
12340             records : records,
12341             totalRecords : totalRecords
12342         };
12343     }
12344 });/*
12345  * Based on:
12346  * Ext JS Library 1.1.1
12347  * Copyright(c) 2006-2007, Ext JS, LLC.
12348  *
12349  * Originally Released Under LGPL - original licence link has changed is not relivant.
12350  *
12351  * Fork - LGPL
12352  * <script type="text/javascript">
12353  */
12354
12355 /**
12356  * @class Roo.data.ArrayReader
12357  * @extends Roo.data.DataReader
12358  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12359  * Each element of that Array represents a row of data fields. The
12360  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12361  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12362  * <p>
12363  * Example code:.
12364  * <pre><code>
12365 var RecordDef = Roo.data.Record.create([
12366     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12367     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12368 ]);
12369 var myReader = new Roo.data.ArrayReader({
12370     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12371 }, RecordDef);
12372 </code></pre>
12373  * <p>
12374  * This would consume an Array like this:
12375  * <pre><code>
12376 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12377   </code></pre>
12378  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12379  * @constructor
12380  * Create a new JsonReader
12381  * @param {Object} meta Metadata configuration options.
12382  * @param {Object} recordType Either an Array of field definition objects
12383  * as specified to {@link Roo.data.Record#create},
12384  * or an {@link Roo.data.Record} object
12385  * created using {@link Roo.data.Record#create}.
12386  */
12387 Roo.data.ArrayReader = function(meta, recordType){
12388     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12389 };
12390
12391 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12392     /**
12393      * Create a data block containing Roo.data.Records from an XML document.
12394      * @param {Object} o An Array of row objects which represents the dataset.
12395      * @return {Object} data A data block which is used by an Roo.data.Store object as
12396      * a cache of Roo.data.Records.
12397      */
12398     readRecords : function(o){
12399         var sid = this.meta ? this.meta.id : null;
12400         var recordType = this.recordType, fields = recordType.prototype.fields;
12401         var records = [];
12402         var root = o;
12403             for(var i = 0; i < root.length; i++){
12404                     var n = root[i];
12405                 var values = {};
12406                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12407                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12408                 var f = fields.items[j];
12409                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12410                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12411                 v = f.convert(v);
12412                 values[f.name] = v;
12413             }
12414                 var record = new recordType(values, id);
12415                 record.json = n;
12416                 records[records.length] = record;
12417             }
12418             return {
12419                 records : records,
12420                 totalRecords : records.length
12421             };
12422     }
12423 });/*
12424  * - LGPL
12425  * * 
12426  */
12427
12428 /**
12429  * @class Roo.bootstrap.ComboBox
12430  * @extends Roo.bootstrap.TriggerField
12431  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12432  * @cfg {Boolean} append (true|false) default false
12433  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12434  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12435  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12436  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12437  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12438  * @cfg {Boolean} animate default true
12439  * @cfg {Boolean} emptyResultText only for touch device
12440  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12441  * @cfg {String} emptyTitle default ''
12442  * @constructor
12443  * Create a new ComboBox.
12444  * @param {Object} config Configuration options
12445  */
12446 Roo.bootstrap.ComboBox = function(config){
12447     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12448     this.addEvents({
12449         /**
12450          * @event expand
12451          * Fires when the dropdown list is expanded
12452         * @param {Roo.bootstrap.ComboBox} combo This combo box
12453         */
12454         'expand' : true,
12455         /**
12456          * @event collapse
12457          * Fires when the dropdown list is collapsed
12458         * @param {Roo.bootstrap.ComboBox} combo This combo box
12459         */
12460         'collapse' : true,
12461         /**
12462          * @event beforeselect
12463          * Fires before a list item is selected. Return false to cancel the selection.
12464         * @param {Roo.bootstrap.ComboBox} combo This combo box
12465         * @param {Roo.data.Record} record The data record returned from the underlying store
12466         * @param {Number} index The index of the selected item in the dropdown list
12467         */
12468         'beforeselect' : true,
12469         /**
12470          * @event select
12471          * Fires when a list item is selected
12472         * @param {Roo.bootstrap.ComboBox} combo This combo box
12473         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12474         * @param {Number} index The index of the selected item in the dropdown list
12475         */
12476         'select' : true,
12477         /**
12478          * @event beforequery
12479          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12480          * The event object passed has these properties:
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         * @param {String} query The query
12483         * @param {Boolean} forceAll true to force "all" query
12484         * @param {Boolean} cancel true to cancel the query
12485         * @param {Object} e The query event object
12486         */
12487         'beforequery': true,
12488          /**
12489          * @event add
12490          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12491         * @param {Roo.bootstrap.ComboBox} combo This combo box
12492         */
12493         'add' : true,
12494         /**
12495          * @event edit
12496          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12497         * @param {Roo.bootstrap.ComboBox} combo This combo box
12498         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12499         */
12500         'edit' : true,
12501         /**
12502          * @event remove
12503          * Fires when the remove value from the combobox array
12504         * @param {Roo.bootstrap.ComboBox} combo This combo box
12505         */
12506         'remove' : true,
12507         /**
12508          * @event afterremove
12509          * Fires when the remove value from the combobox array
12510         * @param {Roo.bootstrap.ComboBox} combo This combo box
12511         */
12512         'afterremove' : true,
12513         /**
12514          * @event specialfilter
12515          * Fires when specialfilter
12516             * @param {Roo.bootstrap.ComboBox} combo This combo box
12517             */
12518         'specialfilter' : true,
12519         /**
12520          * @event tick
12521          * Fires when tick the element
12522             * @param {Roo.bootstrap.ComboBox} combo This combo box
12523             */
12524         'tick' : true,
12525         /**
12526          * @event touchviewdisplay
12527          * Fires when touch view require special display (default is using displayField)
12528             * @param {Roo.bootstrap.ComboBox} combo This combo box
12529             * @param {Object} cfg set html .
12530             */
12531         'touchviewdisplay' : true
12532         
12533     });
12534     
12535     this.item = [];
12536     this.tickItems = [];
12537     
12538     this.selectedIndex = -1;
12539     if(this.mode == 'local'){
12540         if(config.queryDelay === undefined){
12541             this.queryDelay = 10;
12542         }
12543         if(config.minChars === undefined){
12544             this.minChars = 0;
12545         }
12546     }
12547 };
12548
12549 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12550      
12551     /**
12552      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12553      * rendering into an Roo.Editor, defaults to false)
12554      */
12555     /**
12556      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12557      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12558      */
12559     /**
12560      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12561      */
12562     /**
12563      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12564      * the dropdown list (defaults to undefined, with no header element)
12565      */
12566
12567      /**
12568      * @cfg {String/Roo.Template} tpl The template to use to render the output
12569      */
12570      
12571      /**
12572      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12573      */
12574     listWidth: undefined,
12575     /**
12576      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12577      * mode = 'remote' or 'text' if mode = 'local')
12578      */
12579     displayField: undefined,
12580     
12581     /**
12582      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12583      * mode = 'remote' or 'value' if mode = 'local'). 
12584      * Note: use of a valueField requires the user make a selection
12585      * in order for a value to be mapped.
12586      */
12587     valueField: undefined,
12588     /**
12589      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12590      */
12591     modalTitle : '',
12592     
12593     /**
12594      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12595      * field's data value (defaults to the underlying DOM element's name)
12596      */
12597     hiddenName: undefined,
12598     /**
12599      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12600      */
12601     listClass: '',
12602     /**
12603      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12604      */
12605     selectedClass: 'active',
12606     
12607     /**
12608      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12609      */
12610     shadow:'sides',
12611     /**
12612      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12613      * anchor positions (defaults to 'tl-bl')
12614      */
12615     listAlign: 'tl-bl?',
12616     /**
12617      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12618      */
12619     maxHeight: 300,
12620     /**
12621      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12622      * query specified by the allQuery config option (defaults to 'query')
12623      */
12624     triggerAction: 'query',
12625     /**
12626      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12627      * (defaults to 4, does not apply if editable = false)
12628      */
12629     minChars : 4,
12630     /**
12631      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12632      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12633      */
12634     typeAhead: false,
12635     /**
12636      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12637      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12638      */
12639     queryDelay: 500,
12640     /**
12641      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12642      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12643      */
12644     pageSize: 0,
12645     /**
12646      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12647      * when editable = true (defaults to false)
12648      */
12649     selectOnFocus:false,
12650     /**
12651      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12652      */
12653     queryParam: 'query',
12654     /**
12655      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12656      * when mode = 'remote' (defaults to 'Loading...')
12657      */
12658     loadingText: 'Loading...',
12659     /**
12660      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12661      */
12662     resizable: false,
12663     /**
12664      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12665      */
12666     handleHeight : 8,
12667     /**
12668      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12669      * traditional select (defaults to true)
12670      */
12671     editable: true,
12672     /**
12673      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12674      */
12675     allQuery: '',
12676     /**
12677      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12678      */
12679     mode: 'remote',
12680     /**
12681      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12682      * listWidth has a higher value)
12683      */
12684     minListWidth : 70,
12685     /**
12686      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12687      * allow the user to set arbitrary text into the field (defaults to false)
12688      */
12689     forceSelection:false,
12690     /**
12691      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12692      * if typeAhead = true (defaults to 250)
12693      */
12694     typeAheadDelay : 250,
12695     /**
12696      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12697      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12698      */
12699     valueNotFoundText : undefined,
12700     /**
12701      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12702      */
12703     blockFocus : false,
12704     
12705     /**
12706      * @cfg {Boolean} disableClear Disable showing of clear button.
12707      */
12708     disableClear : false,
12709     /**
12710      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12711      */
12712     alwaysQuery : false,
12713     
12714     /**
12715      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12716      */
12717     multiple : false,
12718     
12719     /**
12720      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12721      */
12722     invalidClass : "has-warning",
12723     
12724     /**
12725      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12726      */
12727     validClass : "has-success",
12728     
12729     /**
12730      * @cfg {Boolean} specialFilter (true|false) special filter default false
12731      */
12732     specialFilter : false,
12733     
12734     /**
12735      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12736      */
12737     mobileTouchView : true,
12738     
12739     /**
12740      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12741      */
12742     useNativeIOS : false,
12743     
12744     ios_options : false,
12745     
12746     //private
12747     addicon : false,
12748     editicon: false,
12749     
12750     page: 0,
12751     hasQuery: false,
12752     append: false,
12753     loadNext: false,
12754     autoFocus : true,
12755     tickable : false,
12756     btnPosition : 'right',
12757     triggerList : true,
12758     showToggleBtn : true,
12759     animate : true,
12760     emptyResultText: 'Empty',
12761     triggerText : 'Select',
12762     emptyTitle : '',
12763     
12764     // element that contains real text value.. (when hidden is used..)
12765     
12766     getAutoCreate : function()
12767     {   
12768         var cfg = false;
12769         //render
12770         /*
12771          * Render classic select for iso
12772          */
12773         
12774         if(Roo.isIOS && this.useNativeIOS){
12775             cfg = this.getAutoCreateNativeIOS();
12776             return cfg;
12777         }
12778         
12779         /*
12780          * Touch Devices
12781          */
12782         
12783         if(Roo.isTouch && this.mobileTouchView){
12784             cfg = this.getAutoCreateTouchView();
12785             return cfg;;
12786         }
12787         
12788         /*
12789          *  Normal ComboBox
12790          */
12791         if(!this.tickable){
12792             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12793             return cfg;
12794         }
12795         
12796         /*
12797          *  ComboBox with tickable selections
12798          */
12799              
12800         var align = this.labelAlign || this.parentLabelAlign();
12801         
12802         cfg = {
12803             cls : 'form-group roo-combobox-tickable' //input-group
12804         };
12805         
12806         var btn_text_select = '';
12807         var btn_text_done = '';
12808         var btn_text_cancel = '';
12809         
12810         if (this.btn_text_show) {
12811             btn_text_select = 'Select';
12812             btn_text_done = 'Done';
12813             btn_text_cancel = 'Cancel'; 
12814         }
12815         
12816         var buttons = {
12817             tag : 'div',
12818             cls : 'tickable-buttons',
12819             cn : [
12820                 {
12821                     tag : 'button',
12822                     type : 'button',
12823                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12824                     //html : this.triggerText
12825                     html: btn_text_select
12826                 },
12827                 {
12828                     tag : 'button',
12829                     type : 'button',
12830                     name : 'ok',
12831                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12832                     //html : 'Done'
12833                     html: btn_text_done
12834                 },
12835                 {
12836                     tag : 'button',
12837                     type : 'button',
12838                     name : 'cancel',
12839                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12840                     //html : 'Cancel'
12841                     html: btn_text_cancel
12842                 }
12843             ]
12844         };
12845         
12846         if(this.editable){
12847             buttons.cn.unshift({
12848                 tag: 'input',
12849                 cls: 'roo-select2-search-field-input'
12850             });
12851         }
12852         
12853         var _this = this;
12854         
12855         Roo.each(buttons.cn, function(c){
12856             if (_this.size) {
12857                 c.cls += ' btn-' + _this.size;
12858             }
12859
12860             if (_this.disabled) {
12861                 c.disabled = true;
12862             }
12863         });
12864         
12865         var box = {
12866             tag: 'div',
12867             cn: [
12868                 {
12869                     tag: 'input',
12870                     type : 'hidden',
12871                     cls: 'form-hidden-field'
12872                 },
12873                 {
12874                     tag: 'ul',
12875                     cls: 'roo-select2-choices',
12876                     cn:[
12877                         {
12878                             tag: 'li',
12879                             cls: 'roo-select2-search-field',
12880                             cn: [
12881                                 buttons
12882                             ]
12883                         }
12884                     ]
12885                 }
12886             ]
12887         };
12888         
12889         var combobox = {
12890             cls: 'roo-select2-container input-group roo-select2-container-multi',
12891             cn: [
12892                 box
12893 //                {
12894 //                    tag: 'ul',
12895 //                    cls: 'typeahead typeahead-long dropdown-menu',
12896 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12897 //                }
12898             ]
12899         };
12900         
12901         if(this.hasFeedback && !this.allowBlank){
12902             
12903             var feedback = {
12904                 tag: 'span',
12905                 cls: 'glyphicon form-control-feedback'
12906             };
12907
12908             combobox.cn.push(feedback);
12909         }
12910         
12911         
12912         if (align ==='left' && this.fieldLabel.length) {
12913             
12914             cfg.cls += ' roo-form-group-label-left';
12915             
12916             cfg.cn = [
12917                 {
12918                     tag : 'i',
12919                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12920                     tooltip : 'This field is required'
12921                 },
12922                 {
12923                     tag: 'label',
12924                     'for' :  id,
12925                     cls : 'control-label',
12926                     html : this.fieldLabel
12927
12928                 },
12929                 {
12930                     cls : "", 
12931                     cn: [
12932                         combobox
12933                     ]
12934                 }
12935
12936             ];
12937             
12938             var labelCfg = cfg.cn[1];
12939             var contentCfg = cfg.cn[2];
12940             
12941
12942             if(this.indicatorpos == 'right'){
12943                 
12944                 cfg.cn = [
12945                     {
12946                         tag: 'label',
12947                         'for' :  id,
12948                         cls : 'control-label',
12949                         cn : [
12950                             {
12951                                 tag : 'span',
12952                                 html : this.fieldLabel
12953                             },
12954                             {
12955                                 tag : 'i',
12956                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12957                                 tooltip : 'This field is required'
12958                             }
12959                         ]
12960                     },
12961                     {
12962                         cls : "",
12963                         cn: [
12964                             combobox
12965                         ]
12966                     }
12967
12968                 ];
12969                 
12970                 
12971                 
12972                 labelCfg = cfg.cn[0];
12973                 contentCfg = cfg.cn[1];
12974             
12975             }
12976             
12977             if(this.labelWidth > 12){
12978                 labelCfg.style = "width: " + this.labelWidth + 'px';
12979             }
12980             
12981             if(this.labelWidth < 13 && this.labelmd == 0){
12982                 this.labelmd = this.labelWidth;
12983             }
12984             
12985             if(this.labellg > 0){
12986                 labelCfg.cls += ' col-lg-' + this.labellg;
12987                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12988             }
12989             
12990             if(this.labelmd > 0){
12991                 labelCfg.cls += ' col-md-' + this.labelmd;
12992                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12993             }
12994             
12995             if(this.labelsm > 0){
12996                 labelCfg.cls += ' col-sm-' + this.labelsm;
12997                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12998             }
12999             
13000             if(this.labelxs > 0){
13001                 labelCfg.cls += ' col-xs-' + this.labelxs;
13002                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13003             }
13004                 
13005                 
13006         } else if ( this.fieldLabel.length) {
13007 //                Roo.log(" label");
13008                  cfg.cn = [
13009                     {
13010                         tag : 'i',
13011                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13012                         tooltip : 'This field is required'
13013                     },
13014                     {
13015                         tag: 'label',
13016                         //cls : 'input-group-addon',
13017                         html : this.fieldLabel
13018                     },
13019                     combobox
13020                 ];
13021                 
13022                 if(this.indicatorpos == 'right'){
13023                     cfg.cn = [
13024                         {
13025                             tag: 'label',
13026                             //cls : 'input-group-addon',
13027                             html : this.fieldLabel
13028                         },
13029                         {
13030                             tag : 'i',
13031                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13032                             tooltip : 'This field is required'
13033                         },
13034                         combobox
13035                     ];
13036                     
13037                 }
13038
13039         } else {
13040             
13041 //                Roo.log(" no label && no align");
13042                 cfg = combobox
13043                      
13044                 
13045         }
13046          
13047         var settings=this;
13048         ['xs','sm','md','lg'].map(function(size){
13049             if (settings[size]) {
13050                 cfg.cls += ' col-' + size + '-' + settings[size];
13051             }
13052         });
13053         
13054         return cfg;
13055         
13056     },
13057     
13058     _initEventsCalled : false,
13059     
13060     // private
13061     initEvents: function()
13062     {   
13063         if (this._initEventsCalled) { // as we call render... prevent looping...
13064             return;
13065         }
13066         this._initEventsCalled = true;
13067         
13068         if (!this.store) {
13069             throw "can not find store for combo";
13070         }
13071         
13072         this.indicator = this.indicatorEl();
13073         
13074         this.store = Roo.factory(this.store, Roo.data);
13075         this.store.parent = this;
13076         
13077         // if we are building from html. then this element is so complex, that we can not really
13078         // use the rendered HTML.
13079         // so we have to trash and replace the previous code.
13080         if (Roo.XComponent.build_from_html) {
13081             // remove this element....
13082             var e = this.el.dom, k=0;
13083             while (e ) { e = e.previousSibling;  ++k;}
13084
13085             this.el.remove();
13086             
13087             this.el=false;
13088             this.rendered = false;
13089             
13090             this.render(this.parent().getChildContainer(true), k);
13091         }
13092         
13093         if(Roo.isIOS && this.useNativeIOS){
13094             this.initIOSView();
13095             return;
13096         }
13097         
13098         /*
13099          * Touch Devices
13100          */
13101         
13102         if(Roo.isTouch && this.mobileTouchView){
13103             this.initTouchView();
13104             return;
13105         }
13106         
13107         if(this.tickable){
13108             this.initTickableEvents();
13109             return;
13110         }
13111         
13112         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13113         
13114         if(this.hiddenName){
13115             
13116             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13117             
13118             this.hiddenField.dom.value =
13119                 this.hiddenValue !== undefined ? this.hiddenValue :
13120                 this.value !== undefined ? this.value : '';
13121
13122             // prevent input submission
13123             this.el.dom.removeAttribute('name');
13124             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13125              
13126              
13127         }
13128         //if(Roo.isGecko){
13129         //    this.el.dom.setAttribute('autocomplete', 'off');
13130         //}
13131         
13132         var cls = 'x-combo-list';
13133         
13134         //this.list = new Roo.Layer({
13135         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13136         //});
13137         
13138         var _this = this;
13139         
13140         (function(){
13141             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13142             _this.list.setWidth(lw);
13143         }).defer(100);
13144         
13145         this.list.on('mouseover', this.onViewOver, this);
13146         this.list.on('mousemove', this.onViewMove, this);
13147         this.list.on('scroll', this.onViewScroll, this);
13148         
13149         /*
13150         this.list.swallowEvent('mousewheel');
13151         this.assetHeight = 0;
13152
13153         if(this.title){
13154             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13155             this.assetHeight += this.header.getHeight();
13156         }
13157
13158         this.innerList = this.list.createChild({cls:cls+'-inner'});
13159         this.innerList.on('mouseover', this.onViewOver, this);
13160         this.innerList.on('mousemove', this.onViewMove, this);
13161         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13162         
13163         if(this.allowBlank && !this.pageSize && !this.disableClear){
13164             this.footer = this.list.createChild({cls:cls+'-ft'});
13165             this.pageTb = new Roo.Toolbar(this.footer);
13166            
13167         }
13168         if(this.pageSize){
13169             this.footer = this.list.createChild({cls:cls+'-ft'});
13170             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13171                     {pageSize: this.pageSize});
13172             
13173         }
13174         
13175         if (this.pageTb && this.allowBlank && !this.disableClear) {
13176             var _this = this;
13177             this.pageTb.add(new Roo.Toolbar.Fill(), {
13178                 cls: 'x-btn-icon x-btn-clear',
13179                 text: '&#160;',
13180                 handler: function()
13181                 {
13182                     _this.collapse();
13183                     _this.clearValue();
13184                     _this.onSelect(false, -1);
13185                 }
13186             });
13187         }
13188         if (this.footer) {
13189             this.assetHeight += this.footer.getHeight();
13190         }
13191         */
13192             
13193         if(!this.tpl){
13194             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13195         }
13196
13197         this.view = new Roo.View(this.list, this.tpl, {
13198             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13199         });
13200         //this.view.wrapEl.setDisplayed(false);
13201         this.view.on('click', this.onViewClick, this);
13202         
13203         
13204         this.store.on('beforeload', this.onBeforeLoad, this);
13205         this.store.on('load', this.onLoad, this);
13206         this.store.on('loadexception', this.onLoadException, this);
13207         /*
13208         if(this.resizable){
13209             this.resizer = new Roo.Resizable(this.list,  {
13210                pinned:true, handles:'se'
13211             });
13212             this.resizer.on('resize', function(r, w, h){
13213                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13214                 this.listWidth = w;
13215                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13216                 this.restrictHeight();
13217             }, this);
13218             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13219         }
13220         */
13221         if(!this.editable){
13222             this.editable = true;
13223             this.setEditable(false);
13224         }
13225         
13226         /*
13227         
13228         if (typeof(this.events.add.listeners) != 'undefined') {
13229             
13230             this.addicon = this.wrap.createChild(
13231                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13232        
13233             this.addicon.on('click', function(e) {
13234                 this.fireEvent('add', this);
13235             }, this);
13236         }
13237         if (typeof(this.events.edit.listeners) != 'undefined') {
13238             
13239             this.editicon = this.wrap.createChild(
13240                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13241             if (this.addicon) {
13242                 this.editicon.setStyle('margin-left', '40px');
13243             }
13244             this.editicon.on('click', function(e) {
13245                 
13246                 // we fire even  if inothing is selected..
13247                 this.fireEvent('edit', this, this.lastData );
13248                 
13249             }, this);
13250         }
13251         */
13252         
13253         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13254             "up" : function(e){
13255                 this.inKeyMode = true;
13256                 this.selectPrev();
13257             },
13258
13259             "down" : function(e){
13260                 if(!this.isExpanded()){
13261                     this.onTriggerClick();
13262                 }else{
13263                     this.inKeyMode = true;
13264                     this.selectNext();
13265                 }
13266             },
13267
13268             "enter" : function(e){
13269 //                this.onViewClick();
13270                 //return true;
13271                 this.collapse();
13272                 
13273                 if(this.fireEvent("specialkey", this, e)){
13274                     this.onViewClick(false);
13275                 }
13276                 
13277                 return true;
13278             },
13279
13280             "esc" : function(e){
13281                 this.collapse();
13282             },
13283
13284             "tab" : function(e){
13285                 this.collapse();
13286                 
13287                 if(this.fireEvent("specialkey", this, e)){
13288                     this.onViewClick(false);
13289                 }
13290                 
13291                 return true;
13292             },
13293
13294             scope : this,
13295
13296             doRelay : function(foo, bar, hname){
13297                 if(hname == 'down' || this.scope.isExpanded()){
13298                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13299                 }
13300                 return true;
13301             },
13302
13303             forceKeyDown: true
13304         });
13305         
13306         
13307         this.queryDelay = Math.max(this.queryDelay || 10,
13308                 this.mode == 'local' ? 10 : 250);
13309         
13310         
13311         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13312         
13313         if(this.typeAhead){
13314             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13315         }
13316         if(this.editable !== false){
13317             this.inputEl().on("keyup", this.onKeyUp, this);
13318         }
13319         if(this.forceSelection){
13320             this.inputEl().on('blur', this.doForce, this);
13321         }
13322         
13323         if(this.multiple){
13324             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13325             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13326         }
13327     },
13328     
13329     initTickableEvents: function()
13330     {   
13331         this.createList();
13332         
13333         if(this.hiddenName){
13334             
13335             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13336             
13337             this.hiddenField.dom.value =
13338                 this.hiddenValue !== undefined ? this.hiddenValue :
13339                 this.value !== undefined ? this.value : '';
13340
13341             // prevent input submission
13342             this.el.dom.removeAttribute('name');
13343             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13344              
13345              
13346         }
13347         
13348 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13349         
13350         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13351         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13352         if(this.triggerList){
13353             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13354         }
13355          
13356         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13357         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13358         
13359         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13360         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13361         
13362         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13363         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13364         
13365         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13366         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13367         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13368         
13369         this.okBtn.hide();
13370         this.cancelBtn.hide();
13371         
13372         var _this = this;
13373         
13374         (function(){
13375             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13376             _this.list.setWidth(lw);
13377         }).defer(100);
13378         
13379         this.list.on('mouseover', this.onViewOver, this);
13380         this.list.on('mousemove', this.onViewMove, this);
13381         
13382         this.list.on('scroll', this.onViewScroll, this);
13383         
13384         if(!this.tpl){
13385             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13386         }
13387
13388         this.view = new Roo.View(this.list, this.tpl, {
13389             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13390         });
13391         
13392         //this.view.wrapEl.setDisplayed(false);
13393         this.view.on('click', this.onViewClick, this);
13394         
13395         
13396         
13397         this.store.on('beforeload', this.onBeforeLoad, this);
13398         this.store.on('load', this.onLoad, this);
13399         this.store.on('loadexception', this.onLoadException, this);
13400         
13401         if(this.editable){
13402             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13403                 "up" : function(e){
13404                     this.inKeyMode = true;
13405                     this.selectPrev();
13406                 },
13407
13408                 "down" : function(e){
13409                     this.inKeyMode = true;
13410                     this.selectNext();
13411                 },
13412
13413                 "enter" : function(e){
13414                     if(this.fireEvent("specialkey", this, e)){
13415                         this.onViewClick(false);
13416                     }
13417                     
13418                     return true;
13419                 },
13420
13421                 "esc" : function(e){
13422                     this.onTickableFooterButtonClick(e, false, false);
13423                 },
13424
13425                 "tab" : function(e){
13426                     this.fireEvent("specialkey", this, e);
13427                     
13428                     this.onTickableFooterButtonClick(e, false, false);
13429                     
13430                     return true;
13431                 },
13432
13433                 scope : this,
13434
13435                 doRelay : function(e, fn, key){
13436                     if(this.scope.isExpanded()){
13437                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13438                     }
13439                     return true;
13440                 },
13441
13442                 forceKeyDown: true
13443             });
13444         }
13445         
13446         this.queryDelay = Math.max(this.queryDelay || 10,
13447                 this.mode == 'local' ? 10 : 250);
13448         
13449         
13450         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13451         
13452         if(this.typeAhead){
13453             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13454         }
13455         
13456         if(this.editable !== false){
13457             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13458         }
13459         
13460         this.indicator = this.indicatorEl();
13461         
13462         if(this.indicator){
13463             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13464             this.indicator.hide();
13465         }
13466         
13467     },
13468
13469     onDestroy : function(){
13470         if(this.view){
13471             this.view.setStore(null);
13472             this.view.el.removeAllListeners();
13473             this.view.el.remove();
13474             this.view.purgeListeners();
13475         }
13476         if(this.list){
13477             this.list.dom.innerHTML  = '';
13478         }
13479         
13480         if(this.store){
13481             this.store.un('beforeload', this.onBeforeLoad, this);
13482             this.store.un('load', this.onLoad, this);
13483             this.store.un('loadexception', this.onLoadException, this);
13484         }
13485         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13486     },
13487
13488     // private
13489     fireKey : function(e){
13490         if(e.isNavKeyPress() && !this.list.isVisible()){
13491             this.fireEvent("specialkey", this, e);
13492         }
13493     },
13494
13495     // private
13496     onResize: function(w, h){
13497 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13498 //        
13499 //        if(typeof w != 'number'){
13500 //            // we do not handle it!?!?
13501 //            return;
13502 //        }
13503 //        var tw = this.trigger.getWidth();
13504 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13505 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13506 //        var x = w - tw;
13507 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13508 //            
13509 //        //this.trigger.setStyle('left', x+'px');
13510 //        
13511 //        if(this.list && this.listWidth === undefined){
13512 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13513 //            this.list.setWidth(lw);
13514 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13515 //        }
13516         
13517     
13518         
13519     },
13520
13521     /**
13522      * Allow or prevent the user from directly editing the field text.  If false is passed,
13523      * the user will only be able to select from the items defined in the dropdown list.  This method
13524      * is the runtime equivalent of setting the 'editable' config option at config time.
13525      * @param {Boolean} value True to allow the user to directly edit the field text
13526      */
13527     setEditable : function(value){
13528         if(value == this.editable){
13529             return;
13530         }
13531         this.editable = value;
13532         if(!value){
13533             this.inputEl().dom.setAttribute('readOnly', true);
13534             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13535             this.inputEl().addClass('x-combo-noedit');
13536         }else{
13537             this.inputEl().dom.setAttribute('readOnly', false);
13538             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13539             this.inputEl().removeClass('x-combo-noedit');
13540         }
13541     },
13542
13543     // private
13544     
13545     onBeforeLoad : function(combo,opts){
13546         if(!this.hasFocus){
13547             return;
13548         }
13549          if (!opts.add) {
13550             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13551          }
13552         this.restrictHeight();
13553         this.selectedIndex = -1;
13554     },
13555
13556     // private
13557     onLoad : function(){
13558         
13559         this.hasQuery = false;
13560         
13561         if(!this.hasFocus){
13562             return;
13563         }
13564         
13565         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13566             this.loading.hide();
13567         }
13568         
13569         if(this.store.getCount() > 0){
13570             
13571             this.expand();
13572             this.restrictHeight();
13573             if(this.lastQuery == this.allQuery){
13574                 if(this.editable && !this.tickable){
13575                     this.inputEl().dom.select();
13576                 }
13577                 
13578                 if(
13579                     !this.selectByValue(this.value, true) &&
13580                     this.autoFocus && 
13581                     (
13582                         !this.store.lastOptions ||
13583                         typeof(this.store.lastOptions.add) == 'undefined' || 
13584                         this.store.lastOptions.add != true
13585                     )
13586                 ){
13587                     this.select(0, true);
13588                 }
13589             }else{
13590                 if(this.autoFocus){
13591                     this.selectNext();
13592                 }
13593                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13594                     this.taTask.delay(this.typeAheadDelay);
13595                 }
13596             }
13597         }else{
13598             this.onEmptyResults();
13599         }
13600         
13601         //this.el.focus();
13602     },
13603     // private
13604     onLoadException : function()
13605     {
13606         this.hasQuery = false;
13607         
13608         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13609             this.loading.hide();
13610         }
13611         
13612         if(this.tickable && this.editable){
13613             return;
13614         }
13615         
13616         this.collapse();
13617         // only causes errors at present
13618         //Roo.log(this.store.reader.jsonData);
13619         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13620             // fixme
13621             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13622         //}
13623         
13624         
13625     },
13626     // private
13627     onTypeAhead : function(){
13628         if(this.store.getCount() > 0){
13629             var r = this.store.getAt(0);
13630             var newValue = r.data[this.displayField];
13631             var len = newValue.length;
13632             var selStart = this.getRawValue().length;
13633             
13634             if(selStart != len){
13635                 this.setRawValue(newValue);
13636                 this.selectText(selStart, newValue.length);
13637             }
13638         }
13639     },
13640
13641     // private
13642     onSelect : function(record, index){
13643         
13644         if(this.fireEvent('beforeselect', this, record, index) !== false){
13645         
13646             this.setFromData(index > -1 ? record.data : false);
13647             
13648             this.collapse();
13649             this.fireEvent('select', this, record, index);
13650         }
13651     },
13652
13653     /**
13654      * Returns the currently selected field value or empty string if no value is set.
13655      * @return {String} value The selected value
13656      */
13657     getValue : function()
13658     {
13659         if(Roo.isIOS && this.useNativeIOS){
13660             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13661         }
13662         
13663         if(this.multiple){
13664             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13665         }
13666         
13667         if(this.valueField){
13668             return typeof this.value != 'undefined' ? this.value : '';
13669         }else{
13670             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13671         }
13672     },
13673     
13674     getRawValue : function()
13675     {
13676         if(Roo.isIOS && this.useNativeIOS){
13677             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13678         }
13679         
13680         var v = this.inputEl().getValue();
13681         
13682         return v;
13683     },
13684
13685     /**
13686      * Clears any text/value currently set in the field
13687      */
13688     clearValue : function(){
13689         
13690         if(this.hiddenField){
13691             this.hiddenField.dom.value = '';
13692         }
13693         this.value = '';
13694         this.setRawValue('');
13695         this.lastSelectionText = '';
13696         this.lastData = false;
13697         
13698         var close = this.closeTriggerEl();
13699         
13700         if(close){
13701             close.hide();
13702         }
13703         
13704         this.validate();
13705         
13706     },
13707
13708     /**
13709      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13710      * will be displayed in the field.  If the value does not match the data value of an existing item,
13711      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13712      * Otherwise the field will be blank (although the value will still be set).
13713      * @param {String} value The value to match
13714      */
13715     setValue : function(v)
13716     {
13717         if(Roo.isIOS && this.useNativeIOS){
13718             this.setIOSValue(v);
13719             return;
13720         }
13721         
13722         if(this.multiple){
13723             this.syncValue();
13724             return;
13725         }
13726         
13727         var text = v;
13728         if(this.valueField){
13729             var r = this.findRecord(this.valueField, v);
13730             if(r){
13731                 text = r.data[this.displayField];
13732             }else if(this.valueNotFoundText !== undefined){
13733                 text = this.valueNotFoundText;
13734             }
13735         }
13736         this.lastSelectionText = text;
13737         if(this.hiddenField){
13738             this.hiddenField.dom.value = v;
13739         }
13740         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13741         this.value = v;
13742         
13743         var close = this.closeTriggerEl();
13744         
13745         if(close){
13746             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13747         }
13748         
13749         this.validate();
13750     },
13751     /**
13752      * @property {Object} the last set data for the element
13753      */
13754     
13755     lastData : false,
13756     /**
13757      * Sets the value of the field based on a object which is related to the record format for the store.
13758      * @param {Object} value the value to set as. or false on reset?
13759      */
13760     setFromData : function(o){
13761         
13762         if(this.multiple){
13763             this.addItem(o);
13764             return;
13765         }
13766             
13767         var dv = ''; // display value
13768         var vv = ''; // value value..
13769         this.lastData = o;
13770         if (this.displayField) {
13771             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13772         } else {
13773             // this is an error condition!!!
13774             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13775         }
13776         
13777         if(this.valueField){
13778             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13779         }
13780         
13781         var close = this.closeTriggerEl();
13782         
13783         if(close){
13784             if(dv.length || vv * 1 > 0){
13785                 close.show() ;
13786                 this.blockFocus=true;
13787             } else {
13788                 close.hide();
13789             }             
13790         }
13791         
13792         if(this.hiddenField){
13793             this.hiddenField.dom.value = vv;
13794             
13795             this.lastSelectionText = dv;
13796             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13797             this.value = vv;
13798             return;
13799         }
13800         // no hidden field.. - we store the value in 'value', but still display
13801         // display field!!!!
13802         this.lastSelectionText = dv;
13803         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13804         this.value = vv;
13805         
13806         
13807         
13808     },
13809     // private
13810     reset : function(){
13811         // overridden so that last data is reset..
13812         
13813         if(this.multiple){
13814             this.clearItem();
13815             return;
13816         }
13817         
13818         this.setValue(this.originalValue);
13819         //this.clearInvalid();
13820         this.lastData = false;
13821         if (this.view) {
13822             this.view.clearSelections();
13823         }
13824         
13825         this.validate();
13826     },
13827     // private
13828     findRecord : function(prop, value){
13829         var record;
13830         if(this.store.getCount() > 0){
13831             this.store.each(function(r){
13832                 if(r.data[prop] == value){
13833                     record = r;
13834                     return false;
13835                 }
13836                 return true;
13837             });
13838         }
13839         return record;
13840     },
13841     
13842     getName: function()
13843     {
13844         // returns hidden if it's set..
13845         if (!this.rendered) {return ''};
13846         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13847         
13848     },
13849     // private
13850     onViewMove : function(e, t){
13851         this.inKeyMode = false;
13852     },
13853
13854     // private
13855     onViewOver : function(e, t){
13856         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13857             return;
13858         }
13859         var item = this.view.findItemFromChild(t);
13860         
13861         if(item){
13862             var index = this.view.indexOf(item);
13863             this.select(index, false);
13864         }
13865     },
13866
13867     // private
13868     onViewClick : function(view, doFocus, el, e)
13869     {
13870         var index = this.view.getSelectedIndexes()[0];
13871         
13872         var r = this.store.getAt(index);
13873         
13874         if(this.tickable){
13875             
13876             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13877                 return;
13878             }
13879             
13880             var rm = false;
13881             var _this = this;
13882             
13883             Roo.each(this.tickItems, function(v,k){
13884                 
13885                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13886                     Roo.log(v);
13887                     _this.tickItems.splice(k, 1);
13888                     
13889                     if(typeof(e) == 'undefined' && view == false){
13890                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13891                     }
13892                     
13893                     rm = true;
13894                     return;
13895                 }
13896             });
13897             
13898             if(rm){
13899                 return;
13900             }
13901             
13902             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13903                 this.tickItems.push(r.data);
13904             }
13905             
13906             if(typeof(e) == 'undefined' && view == false){
13907                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13908             }
13909                     
13910             return;
13911         }
13912         
13913         if(r){
13914             this.onSelect(r, index);
13915         }
13916         if(doFocus !== false && !this.blockFocus){
13917             this.inputEl().focus();
13918         }
13919     },
13920
13921     // private
13922     restrictHeight : function(){
13923         //this.innerList.dom.style.height = '';
13924         //var inner = this.innerList.dom;
13925         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13926         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13927         //this.list.beginUpdate();
13928         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13929         this.list.alignTo(this.inputEl(), this.listAlign);
13930         this.list.alignTo(this.inputEl(), this.listAlign);
13931         //this.list.endUpdate();
13932     },
13933
13934     // private
13935     onEmptyResults : function(){
13936         
13937         if(this.tickable && this.editable){
13938             this.restrictHeight();
13939             return;
13940         }
13941         
13942         this.collapse();
13943     },
13944
13945     /**
13946      * Returns true if the dropdown list is expanded, else false.
13947      */
13948     isExpanded : function(){
13949         return this.list.isVisible();
13950     },
13951
13952     /**
13953      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13954      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13955      * @param {String} value The data value of the item to select
13956      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13957      * selected item if it is not currently in view (defaults to true)
13958      * @return {Boolean} True if the value matched an item in the list, else false
13959      */
13960     selectByValue : function(v, scrollIntoView){
13961         if(v !== undefined && v !== null){
13962             var r = this.findRecord(this.valueField || this.displayField, v);
13963             if(r){
13964                 this.select(this.store.indexOf(r), scrollIntoView);
13965                 return true;
13966             }
13967         }
13968         return false;
13969     },
13970
13971     /**
13972      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13973      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13974      * @param {Number} index The zero-based index of the list item to select
13975      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13976      * selected item if it is not currently in view (defaults to true)
13977      */
13978     select : function(index, scrollIntoView){
13979         this.selectedIndex = index;
13980         this.view.select(index);
13981         if(scrollIntoView !== false){
13982             var el = this.view.getNode(index);
13983             /*
13984              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13985              */
13986             if(el){
13987                 this.list.scrollChildIntoView(el, false);
13988             }
13989         }
13990     },
13991
13992     // private
13993     selectNext : function(){
13994         var ct = this.store.getCount();
13995         if(ct > 0){
13996             if(this.selectedIndex == -1){
13997                 this.select(0);
13998             }else if(this.selectedIndex < ct-1){
13999                 this.select(this.selectedIndex+1);
14000             }
14001         }
14002     },
14003
14004     // private
14005     selectPrev : function(){
14006         var ct = this.store.getCount();
14007         if(ct > 0){
14008             if(this.selectedIndex == -1){
14009                 this.select(0);
14010             }else if(this.selectedIndex != 0){
14011                 this.select(this.selectedIndex-1);
14012             }
14013         }
14014     },
14015
14016     // private
14017     onKeyUp : function(e){
14018         if(this.editable !== false && !e.isSpecialKey()){
14019             this.lastKey = e.getKey();
14020             this.dqTask.delay(this.queryDelay);
14021         }
14022     },
14023
14024     // private
14025     validateBlur : function(){
14026         return !this.list || !this.list.isVisible();   
14027     },
14028
14029     // private
14030     initQuery : function(){
14031         
14032         var v = this.getRawValue();
14033         
14034         if(this.tickable && this.editable){
14035             v = this.tickableInputEl().getValue();
14036         }
14037         
14038         this.doQuery(v);
14039     },
14040
14041     // private
14042     doForce : function(){
14043         if(this.inputEl().dom.value.length > 0){
14044             this.inputEl().dom.value =
14045                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14046              
14047         }
14048     },
14049
14050     /**
14051      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14052      * query allowing the query action to be canceled if needed.
14053      * @param {String} query The SQL query to execute
14054      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14055      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14056      * saved in the current store (defaults to false)
14057      */
14058     doQuery : function(q, forceAll){
14059         
14060         if(q === undefined || q === null){
14061             q = '';
14062         }
14063         var qe = {
14064             query: q,
14065             forceAll: forceAll,
14066             combo: this,
14067             cancel:false
14068         };
14069         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14070             return false;
14071         }
14072         q = qe.query;
14073         
14074         forceAll = qe.forceAll;
14075         if(forceAll === true || (q.length >= this.minChars)){
14076             
14077             this.hasQuery = true;
14078             
14079             if(this.lastQuery != q || this.alwaysQuery){
14080                 this.lastQuery = q;
14081                 if(this.mode == 'local'){
14082                     this.selectedIndex = -1;
14083                     if(forceAll){
14084                         this.store.clearFilter();
14085                     }else{
14086                         
14087                         if(this.specialFilter){
14088                             this.fireEvent('specialfilter', this);
14089                             this.onLoad();
14090                             return;
14091                         }
14092                         
14093                         this.store.filter(this.displayField, q);
14094                     }
14095                     
14096                     this.store.fireEvent("datachanged", this.store);
14097                     
14098                     this.onLoad();
14099                     
14100                     
14101                 }else{
14102                     
14103                     this.store.baseParams[this.queryParam] = q;
14104                     
14105                     var options = {params : this.getParams(q)};
14106                     
14107                     if(this.loadNext){
14108                         options.add = true;
14109                         options.params.start = this.page * this.pageSize;
14110                     }
14111                     
14112                     this.store.load(options);
14113                     
14114                     /*
14115                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14116                      *  we should expand the list on onLoad
14117                      *  so command out it
14118                      */
14119 //                    this.expand();
14120                 }
14121             }else{
14122                 this.selectedIndex = -1;
14123                 this.onLoad();   
14124             }
14125         }
14126         
14127         this.loadNext = false;
14128     },
14129     
14130     // private
14131     getParams : function(q){
14132         var p = {};
14133         //p[this.queryParam] = q;
14134         
14135         if(this.pageSize){
14136             p.start = 0;
14137             p.limit = this.pageSize;
14138         }
14139         return p;
14140     },
14141
14142     /**
14143      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14144      */
14145     collapse : function(){
14146         if(!this.isExpanded()){
14147             return;
14148         }
14149         
14150         this.list.hide();
14151         
14152         this.hasFocus = false;
14153         
14154         if(this.tickable){
14155             this.okBtn.hide();
14156             this.cancelBtn.hide();
14157             this.trigger.show();
14158             
14159             if(this.editable){
14160                 this.tickableInputEl().dom.value = '';
14161                 this.tickableInputEl().blur();
14162             }
14163             
14164         }
14165         
14166         Roo.get(document).un('mousedown', this.collapseIf, this);
14167         Roo.get(document).un('mousewheel', this.collapseIf, this);
14168         if (!this.editable) {
14169             Roo.get(document).un('keydown', this.listKeyPress, this);
14170         }
14171         this.fireEvent('collapse', this);
14172         
14173         this.validate();
14174     },
14175
14176     // private
14177     collapseIf : function(e){
14178         var in_combo  = e.within(this.el);
14179         var in_list =  e.within(this.list);
14180         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14181         
14182         if (in_combo || in_list || is_list) {
14183             //e.stopPropagation();
14184             return;
14185         }
14186         
14187         if(this.tickable){
14188             this.onTickableFooterButtonClick(e, false, false);
14189         }
14190
14191         this.collapse();
14192         
14193     },
14194
14195     /**
14196      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14197      */
14198     expand : function(){
14199        
14200         if(this.isExpanded() || !this.hasFocus){
14201             return;
14202         }
14203         
14204         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14205         this.list.setWidth(lw);
14206         
14207         Roo.log('expand');
14208         
14209         this.list.show();
14210         
14211         this.restrictHeight();
14212         
14213         if(this.tickable){
14214             
14215             this.tickItems = Roo.apply([], this.item);
14216             
14217             this.okBtn.show();
14218             this.cancelBtn.show();
14219             this.trigger.hide();
14220             
14221             if(this.editable){
14222                 this.tickableInputEl().focus();
14223             }
14224             
14225         }
14226         
14227         Roo.get(document).on('mousedown', this.collapseIf, this);
14228         Roo.get(document).on('mousewheel', this.collapseIf, this);
14229         if (!this.editable) {
14230             Roo.get(document).on('keydown', this.listKeyPress, this);
14231         }
14232         
14233         this.fireEvent('expand', this);
14234     },
14235
14236     // private
14237     // Implements the default empty TriggerField.onTriggerClick function
14238     onTriggerClick : function(e)
14239     {
14240         Roo.log('trigger click');
14241         
14242         if(this.disabled || !this.triggerList){
14243             return;
14244         }
14245         
14246         this.page = 0;
14247         this.loadNext = false;
14248         
14249         if(this.isExpanded()){
14250             this.collapse();
14251             if (!this.blockFocus) {
14252                 this.inputEl().focus();
14253             }
14254             
14255         }else {
14256             this.hasFocus = true;
14257             if(this.triggerAction == 'all') {
14258                 this.doQuery(this.allQuery, true);
14259             } else {
14260                 this.doQuery(this.getRawValue());
14261             }
14262             if (!this.blockFocus) {
14263                 this.inputEl().focus();
14264             }
14265         }
14266     },
14267     
14268     onTickableTriggerClick : function(e)
14269     {
14270         if(this.disabled){
14271             return;
14272         }
14273         
14274         this.page = 0;
14275         this.loadNext = false;
14276         this.hasFocus = true;
14277         
14278         if(this.triggerAction == 'all') {
14279             this.doQuery(this.allQuery, true);
14280         } else {
14281             this.doQuery(this.getRawValue());
14282         }
14283     },
14284     
14285     onSearchFieldClick : function(e)
14286     {
14287         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14288             this.onTickableFooterButtonClick(e, false, false);
14289             return;
14290         }
14291         
14292         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14293             return;
14294         }
14295         
14296         this.page = 0;
14297         this.loadNext = false;
14298         this.hasFocus = true;
14299         
14300         if(this.triggerAction == 'all') {
14301             this.doQuery(this.allQuery, true);
14302         } else {
14303             this.doQuery(this.getRawValue());
14304         }
14305     },
14306     
14307     listKeyPress : function(e)
14308     {
14309         //Roo.log('listkeypress');
14310         // scroll to first matching element based on key pres..
14311         if (e.isSpecialKey()) {
14312             return false;
14313         }
14314         var k = String.fromCharCode(e.getKey()).toUpperCase();
14315         //Roo.log(k);
14316         var match  = false;
14317         var csel = this.view.getSelectedNodes();
14318         var cselitem = false;
14319         if (csel.length) {
14320             var ix = this.view.indexOf(csel[0]);
14321             cselitem  = this.store.getAt(ix);
14322             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14323                 cselitem = false;
14324             }
14325             
14326         }
14327         
14328         this.store.each(function(v) { 
14329             if (cselitem) {
14330                 // start at existing selection.
14331                 if (cselitem.id == v.id) {
14332                     cselitem = false;
14333                 }
14334                 return true;
14335             }
14336                 
14337             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14338                 match = this.store.indexOf(v);
14339                 return false;
14340             }
14341             return true;
14342         }, this);
14343         
14344         if (match === false) {
14345             return true; // no more action?
14346         }
14347         // scroll to?
14348         this.view.select(match);
14349         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14350         sn.scrollIntoView(sn.dom.parentNode, false);
14351     },
14352     
14353     onViewScroll : function(e, t){
14354         
14355         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){
14356             return;
14357         }
14358         
14359         this.hasQuery = true;
14360         
14361         this.loading = this.list.select('.loading', true).first();
14362         
14363         if(this.loading === null){
14364             this.list.createChild({
14365                 tag: 'div',
14366                 cls: 'loading roo-select2-more-results roo-select2-active',
14367                 html: 'Loading more results...'
14368             });
14369             
14370             this.loading = this.list.select('.loading', true).first();
14371             
14372             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14373             
14374             this.loading.hide();
14375         }
14376         
14377         this.loading.show();
14378         
14379         var _combo = this;
14380         
14381         this.page++;
14382         this.loadNext = true;
14383         
14384         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14385         
14386         return;
14387     },
14388     
14389     addItem : function(o)
14390     {   
14391         var dv = ''; // display value
14392         
14393         if (this.displayField) {
14394             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14395         } else {
14396             // this is an error condition!!!
14397             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14398         }
14399         
14400         if(!dv.length){
14401             return;
14402         }
14403         
14404         var choice = this.choices.createChild({
14405             tag: 'li',
14406             cls: 'roo-select2-search-choice',
14407             cn: [
14408                 {
14409                     tag: 'div',
14410                     html: dv
14411                 },
14412                 {
14413                     tag: 'a',
14414                     href: '#',
14415                     cls: 'roo-select2-search-choice-close fa fa-times',
14416                     tabindex: '-1'
14417                 }
14418             ]
14419             
14420         }, this.searchField);
14421         
14422         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14423         
14424         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14425         
14426         this.item.push(o);
14427         
14428         this.lastData = o;
14429         
14430         this.syncValue();
14431         
14432         this.inputEl().dom.value = '';
14433         
14434         this.validate();
14435     },
14436     
14437     onRemoveItem : function(e, _self, o)
14438     {
14439         e.preventDefault();
14440         
14441         this.lastItem = Roo.apply([], this.item);
14442         
14443         var index = this.item.indexOf(o.data) * 1;
14444         
14445         if( index < 0){
14446             Roo.log('not this item?!');
14447             return;
14448         }
14449         
14450         this.item.splice(index, 1);
14451         o.item.remove();
14452         
14453         this.syncValue();
14454         
14455         this.fireEvent('remove', this, e);
14456         
14457         this.validate();
14458         
14459     },
14460     
14461     syncValue : function()
14462     {
14463         if(!this.item.length){
14464             this.clearValue();
14465             return;
14466         }
14467             
14468         var value = [];
14469         var _this = this;
14470         Roo.each(this.item, function(i){
14471             if(_this.valueField){
14472                 value.push(i[_this.valueField]);
14473                 return;
14474             }
14475
14476             value.push(i);
14477         });
14478
14479         this.value = value.join(',');
14480
14481         if(this.hiddenField){
14482             this.hiddenField.dom.value = this.value;
14483         }
14484         
14485         this.store.fireEvent("datachanged", this.store);
14486         
14487         this.validate();
14488     },
14489     
14490     clearItem : function()
14491     {
14492         if(!this.multiple){
14493             return;
14494         }
14495         
14496         this.item = [];
14497         
14498         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14499            c.remove();
14500         });
14501         
14502         this.syncValue();
14503         
14504         this.validate();
14505         
14506         if(this.tickable && !Roo.isTouch){
14507             this.view.refresh();
14508         }
14509     },
14510     
14511     inputEl: function ()
14512     {
14513         if(Roo.isIOS && this.useNativeIOS){
14514             return this.el.select('select.roo-ios-select', true).first();
14515         }
14516         
14517         if(Roo.isTouch && this.mobileTouchView){
14518             return this.el.select('input.form-control',true).first();
14519         }
14520         
14521         if(this.tickable){
14522             return this.searchField;
14523         }
14524         
14525         return this.el.select('input.form-control',true).first();
14526     },
14527     
14528     onTickableFooterButtonClick : function(e, btn, el)
14529     {
14530         e.preventDefault();
14531         
14532         this.lastItem = Roo.apply([], this.item);
14533         
14534         if(btn && btn.name == 'cancel'){
14535             this.tickItems = Roo.apply([], this.item);
14536             this.collapse();
14537             return;
14538         }
14539         
14540         this.clearItem();
14541         
14542         var _this = this;
14543         
14544         Roo.each(this.tickItems, function(o){
14545             _this.addItem(o);
14546         });
14547         
14548         this.collapse();
14549         
14550     },
14551     
14552     validate : function()
14553     {
14554         var v = this.getRawValue();
14555         
14556         if(this.multiple){
14557             v = this.getValue();
14558         }
14559         
14560         if(this.disabled || this.allowBlank || v.length){
14561             this.markValid();
14562             return true;
14563         }
14564         
14565         this.markInvalid();
14566         return false;
14567     },
14568     
14569     tickableInputEl : function()
14570     {
14571         if(!this.tickable || !this.editable){
14572             return this.inputEl();
14573         }
14574         
14575         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14576     },
14577     
14578     
14579     getAutoCreateTouchView : function()
14580     {
14581         var id = Roo.id();
14582         
14583         var cfg = {
14584             cls: 'form-group' //input-group
14585         };
14586         
14587         var input =  {
14588             tag: 'input',
14589             id : id,
14590             type : this.inputType,
14591             cls : 'form-control x-combo-noedit',
14592             autocomplete: 'new-password',
14593             placeholder : this.placeholder || '',
14594             readonly : true
14595         };
14596         
14597         if (this.name) {
14598             input.name = this.name;
14599         }
14600         
14601         if (this.size) {
14602             input.cls += ' input-' + this.size;
14603         }
14604         
14605         if (this.disabled) {
14606             input.disabled = true;
14607         }
14608         
14609         var inputblock = {
14610             cls : '',
14611             cn : [
14612                 input
14613             ]
14614         };
14615         
14616         if(this.before){
14617             inputblock.cls += ' input-group';
14618             
14619             inputblock.cn.unshift({
14620                 tag :'span',
14621                 cls : 'input-group-addon',
14622                 html : this.before
14623             });
14624         }
14625         
14626         if(this.removable && !this.multiple){
14627             inputblock.cls += ' roo-removable';
14628             
14629             inputblock.cn.push({
14630                 tag: 'button',
14631                 html : 'x',
14632                 cls : 'roo-combo-removable-btn close'
14633             });
14634         }
14635
14636         if(this.hasFeedback && !this.allowBlank){
14637             
14638             inputblock.cls += ' has-feedback';
14639             
14640             inputblock.cn.push({
14641                 tag: 'span',
14642                 cls: 'glyphicon form-control-feedback'
14643             });
14644             
14645         }
14646         
14647         if (this.after) {
14648             
14649             inputblock.cls += (this.before) ? '' : ' input-group';
14650             
14651             inputblock.cn.push({
14652                 tag :'span',
14653                 cls : 'input-group-addon',
14654                 html : this.after
14655             });
14656         }
14657
14658         var box = {
14659             tag: 'div',
14660             cn: [
14661                 {
14662                     tag: 'input',
14663                     type : 'hidden',
14664                     cls: 'form-hidden-field'
14665                 },
14666                 inputblock
14667             ]
14668             
14669         };
14670         
14671         if(this.multiple){
14672             box = {
14673                 tag: 'div',
14674                 cn: [
14675                     {
14676                         tag: 'input',
14677                         type : 'hidden',
14678                         cls: 'form-hidden-field'
14679                     },
14680                     {
14681                         tag: 'ul',
14682                         cls: 'roo-select2-choices',
14683                         cn:[
14684                             {
14685                                 tag: 'li',
14686                                 cls: 'roo-select2-search-field',
14687                                 cn: [
14688
14689                                     inputblock
14690                                 ]
14691                             }
14692                         ]
14693                     }
14694                 ]
14695             }
14696         };
14697         
14698         var combobox = {
14699             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14700             cn: [
14701                 box
14702             ]
14703         };
14704         
14705         if(!this.multiple && this.showToggleBtn){
14706             
14707             var caret = {
14708                         tag: 'span',
14709                         cls: 'caret'
14710             };
14711             
14712             if (this.caret != false) {
14713                 caret = {
14714                      tag: 'i',
14715                      cls: 'fa fa-' + this.caret
14716                 };
14717                 
14718             }
14719             
14720             combobox.cn.push({
14721                 tag :'span',
14722                 cls : 'input-group-addon btn dropdown-toggle',
14723                 cn : [
14724                     caret,
14725                     {
14726                         tag: 'span',
14727                         cls: 'combobox-clear',
14728                         cn  : [
14729                             {
14730                                 tag : 'i',
14731                                 cls: 'icon-remove'
14732                             }
14733                         ]
14734                     }
14735                 ]
14736
14737             })
14738         }
14739         
14740         if(this.multiple){
14741             combobox.cls += ' roo-select2-container-multi';
14742         }
14743         
14744         var align = this.labelAlign || this.parentLabelAlign();
14745         
14746         if (align ==='left' && this.fieldLabel.length) {
14747
14748             cfg.cn = [
14749                 {
14750                    tag : 'i',
14751                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14752                    tooltip : 'This field is required'
14753                 },
14754                 {
14755                     tag: 'label',
14756                     cls : 'control-label',
14757                     html : this.fieldLabel
14758
14759                 },
14760                 {
14761                     cls : '', 
14762                     cn: [
14763                         combobox
14764                     ]
14765                 }
14766             ];
14767             
14768             var labelCfg = cfg.cn[1];
14769             var contentCfg = cfg.cn[2];
14770             
14771
14772             if(this.indicatorpos == 'right'){
14773                 cfg.cn = [
14774                     {
14775                         tag: 'label',
14776                         'for' :  id,
14777                         cls : 'control-label',
14778                         cn : [
14779                             {
14780                                 tag : 'span',
14781                                 html : this.fieldLabel
14782                             },
14783                             {
14784                                 tag : 'i',
14785                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14786                                 tooltip : 'This field is required'
14787                             }
14788                         ]
14789                     },
14790                     {
14791                         cls : "",
14792                         cn: [
14793                             combobox
14794                         ]
14795                     }
14796
14797                 ];
14798                 
14799                 labelCfg = cfg.cn[0];
14800                 contentCfg = cfg.cn[1];
14801             }
14802             
14803            
14804             
14805             if(this.labelWidth > 12){
14806                 labelCfg.style = "width: " + this.labelWidth + 'px';
14807             }
14808             
14809             if(this.labelWidth < 13 && this.labelmd == 0){
14810                 this.labelmd = this.labelWidth;
14811             }
14812             
14813             if(this.labellg > 0){
14814                 labelCfg.cls += ' col-lg-' + this.labellg;
14815                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14816             }
14817             
14818             if(this.labelmd > 0){
14819                 labelCfg.cls += ' col-md-' + this.labelmd;
14820                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14821             }
14822             
14823             if(this.labelsm > 0){
14824                 labelCfg.cls += ' col-sm-' + this.labelsm;
14825                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14826             }
14827             
14828             if(this.labelxs > 0){
14829                 labelCfg.cls += ' col-xs-' + this.labelxs;
14830                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14831             }
14832                 
14833                 
14834         } else if ( this.fieldLabel.length) {
14835             cfg.cn = [
14836                 {
14837                    tag : 'i',
14838                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14839                    tooltip : 'This field is required'
14840                 },
14841                 {
14842                     tag: 'label',
14843                     cls : 'control-label',
14844                     html : this.fieldLabel
14845
14846                 },
14847                 {
14848                     cls : '', 
14849                     cn: [
14850                         combobox
14851                     ]
14852                 }
14853             ];
14854             
14855             if(this.indicatorpos == 'right'){
14856                 cfg.cn = [
14857                     {
14858                         tag: 'label',
14859                         cls : 'control-label',
14860                         html : this.fieldLabel,
14861                         cn : [
14862                             {
14863                                tag : 'i',
14864                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14865                                tooltip : 'This field is required'
14866                             }
14867                         ]
14868                     },
14869                     {
14870                         cls : '', 
14871                         cn: [
14872                             combobox
14873                         ]
14874                     }
14875                 ];
14876             }
14877         } else {
14878             cfg.cn = combobox;    
14879         }
14880         
14881         
14882         var settings = this;
14883         
14884         ['xs','sm','md','lg'].map(function(size){
14885             if (settings[size]) {
14886                 cfg.cls += ' col-' + size + '-' + settings[size];
14887             }
14888         });
14889         
14890         return cfg;
14891     },
14892     
14893     initTouchView : function()
14894     {
14895         this.renderTouchView();
14896         
14897         this.touchViewEl.on('scroll', function(){
14898             this.el.dom.scrollTop = 0;
14899         }, this);
14900         
14901         this.originalValue = this.getValue();
14902         
14903         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14904         
14905         this.inputEl().on("click", this.showTouchView, this);
14906         if (this.triggerEl) {
14907             this.triggerEl.on("click", this.showTouchView, this);
14908         }
14909         
14910         
14911         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14912         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14913         
14914         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14915         
14916         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14917         this.store.on('load', this.onTouchViewLoad, this);
14918         this.store.on('loadexception', this.onTouchViewLoadException, this);
14919         
14920         if(this.hiddenName){
14921             
14922             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14923             
14924             this.hiddenField.dom.value =
14925                 this.hiddenValue !== undefined ? this.hiddenValue :
14926                 this.value !== undefined ? this.value : '';
14927         
14928             this.el.dom.removeAttribute('name');
14929             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14930         }
14931         
14932         if(this.multiple){
14933             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14934             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14935         }
14936         
14937         if(this.removable && !this.multiple){
14938             var close = this.closeTriggerEl();
14939             if(close){
14940                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14941                 close.on('click', this.removeBtnClick, this, close);
14942             }
14943         }
14944         /*
14945          * fix the bug in Safari iOS8
14946          */
14947         this.inputEl().on("focus", function(e){
14948             document.activeElement.blur();
14949         }, this);
14950         
14951         return;
14952         
14953         
14954     },
14955     
14956     renderTouchView : function()
14957     {
14958         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14959         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14960         
14961         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14962         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14963         
14964         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14965         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14966         this.touchViewBodyEl.setStyle('overflow', 'auto');
14967         
14968         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14969         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14970         
14971         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14972         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14973         
14974     },
14975     
14976     showTouchView : function()
14977     {
14978         if(this.disabled){
14979             return;
14980         }
14981         
14982         this.touchViewHeaderEl.hide();
14983
14984         if(this.modalTitle.length){
14985             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14986             this.touchViewHeaderEl.show();
14987         }
14988
14989         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14990         this.touchViewEl.show();
14991
14992         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14993         
14994         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14995         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14996
14997         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14998
14999         if(this.modalTitle.length){
15000             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15001         }
15002         
15003         this.touchViewBodyEl.setHeight(bodyHeight);
15004
15005         if(this.animate){
15006             var _this = this;
15007             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15008         }else{
15009             this.touchViewEl.addClass('in');
15010         }
15011
15012         this.doTouchViewQuery();
15013         
15014     },
15015     
15016     hideTouchView : function()
15017     {
15018         this.touchViewEl.removeClass('in');
15019
15020         if(this.animate){
15021             var _this = this;
15022             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15023         }else{
15024             this.touchViewEl.setStyle('display', 'none');
15025         }
15026         
15027     },
15028     
15029     setTouchViewValue : function()
15030     {
15031         if(this.multiple){
15032             this.clearItem();
15033         
15034             var _this = this;
15035
15036             Roo.each(this.tickItems, function(o){
15037                 this.addItem(o);
15038             }, this);
15039         }
15040         
15041         this.hideTouchView();
15042     },
15043     
15044     doTouchViewQuery : function()
15045     {
15046         var qe = {
15047             query: '',
15048             forceAll: true,
15049             combo: this,
15050             cancel:false
15051         };
15052         
15053         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15054             return false;
15055         }
15056         
15057         if(!this.alwaysQuery || this.mode == 'local'){
15058             this.onTouchViewLoad();
15059             return;
15060         }
15061         
15062         this.store.load();
15063     },
15064     
15065     onTouchViewBeforeLoad : function(combo,opts)
15066     {
15067         return;
15068     },
15069
15070     // private
15071     onTouchViewLoad : function()
15072     {
15073         if(this.store.getCount() < 1){
15074             this.onTouchViewEmptyResults();
15075             return;
15076         }
15077         
15078         this.clearTouchView();
15079         
15080         var rawValue = this.getRawValue();
15081         
15082         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15083         
15084         this.tickItems = [];
15085         
15086         this.store.data.each(function(d, rowIndex){
15087             var row = this.touchViewListGroup.createChild(template);
15088             
15089             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15090                 row.addClass(d.data.cls);
15091             }
15092             
15093             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15094                 var cfg = {
15095                     data : d.data,
15096                     html : d.data[this.displayField]
15097                 };
15098                 
15099                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15100                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15101                 }
15102             }
15103             row.removeClass('selected');
15104             if(!this.multiple && this.valueField &&
15105                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15106             {
15107                 // radio buttons..
15108                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15109                 row.addClass('selected');
15110             }
15111             
15112             if(this.multiple && this.valueField &&
15113                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15114             {
15115                 
15116                 // checkboxes...
15117                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15118                 this.tickItems.push(d.data);
15119             }
15120             
15121             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15122             
15123         }, this);
15124         
15125         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15126         
15127         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15128
15129         if(this.modalTitle.length){
15130             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15131         }
15132
15133         var listHeight = this.touchViewListGroup.getHeight();
15134         
15135         var _this = this;
15136         
15137         if(firstChecked && listHeight > bodyHeight){
15138             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15139         }
15140         
15141     },
15142     
15143     onTouchViewLoadException : function()
15144     {
15145         this.hideTouchView();
15146     },
15147     
15148     onTouchViewEmptyResults : function()
15149     {
15150         this.clearTouchView();
15151         
15152         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15153         
15154         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15155         
15156     },
15157     
15158     clearTouchView : function()
15159     {
15160         this.touchViewListGroup.dom.innerHTML = '';
15161     },
15162     
15163     onTouchViewClick : function(e, el, o)
15164     {
15165         e.preventDefault();
15166         
15167         var row = o.row;
15168         var rowIndex = o.rowIndex;
15169         
15170         var r = this.store.getAt(rowIndex);
15171         
15172         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15173             
15174             if(!this.multiple){
15175                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15176                     c.dom.removeAttribute('checked');
15177                 }, this);
15178
15179                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15180
15181                 this.setFromData(r.data);
15182
15183                 var close = this.closeTriggerEl();
15184
15185                 if(close){
15186                     close.show();
15187                 }
15188
15189                 this.hideTouchView();
15190
15191                 this.fireEvent('select', this, r, rowIndex);
15192
15193                 return;
15194             }
15195
15196             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15197                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15198                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15199                 return;
15200             }
15201
15202             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15203             this.addItem(r.data);
15204             this.tickItems.push(r.data);
15205         }
15206     },
15207     
15208     getAutoCreateNativeIOS : function()
15209     {
15210         var cfg = {
15211             cls: 'form-group' //input-group,
15212         };
15213         
15214         var combobox =  {
15215             tag: 'select',
15216             cls : 'roo-ios-select'
15217         };
15218         
15219         if (this.name) {
15220             combobox.name = this.name;
15221         }
15222         
15223         if (this.disabled) {
15224             combobox.disabled = true;
15225         }
15226         
15227         var settings = this;
15228         
15229         ['xs','sm','md','lg'].map(function(size){
15230             if (settings[size]) {
15231                 cfg.cls += ' col-' + size + '-' + settings[size];
15232             }
15233         });
15234         
15235         cfg.cn = combobox;
15236         
15237         return cfg;
15238         
15239     },
15240     
15241     initIOSView : function()
15242     {
15243         this.store.on('load', this.onIOSViewLoad, this);
15244         
15245         return;
15246     },
15247     
15248     onIOSViewLoad : function()
15249     {
15250         if(this.store.getCount() < 1){
15251             return;
15252         }
15253         
15254         this.clearIOSView();
15255         
15256         if(this.allowBlank) {
15257             
15258             var default_text = '-- SELECT --';
15259             
15260             if(this.placeholder.length){
15261                 default_text = this.placeholder;
15262             }
15263             
15264             if(this.emptyTitle.length){
15265                 default_text += ' - ' + this.emptyTitle + ' -';
15266             }
15267             
15268             var opt = this.inputEl().createChild({
15269                 tag: 'option',
15270                 value : 0,
15271                 html : default_text
15272             });
15273             
15274             var o = {};
15275             o[this.valueField] = 0;
15276             o[this.displayField] = default_text;
15277             
15278             this.ios_options.push({
15279                 data : o,
15280                 el : opt
15281             });
15282             
15283         }
15284         
15285         this.store.data.each(function(d, rowIndex){
15286             
15287             var html = '';
15288             
15289             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15290                 html = d.data[this.displayField];
15291             }
15292             
15293             var value = '';
15294             
15295             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15296                 value = d.data[this.valueField];
15297             }
15298             
15299             var option = {
15300                 tag: 'option',
15301                 value : value,
15302                 html : html
15303             };
15304             
15305             if(this.value == d.data[this.valueField]){
15306                 option['selected'] = true;
15307             }
15308             
15309             var opt = this.inputEl().createChild(option);
15310             
15311             this.ios_options.push({
15312                 data : d.data,
15313                 el : opt
15314             });
15315             
15316         }, this);
15317         
15318         this.inputEl().on('change', function(){
15319            this.fireEvent('select', this);
15320         }, this);
15321         
15322     },
15323     
15324     clearIOSView: function()
15325     {
15326         this.inputEl().dom.innerHTML = '';
15327         
15328         this.ios_options = [];
15329     },
15330     
15331     setIOSValue: function(v)
15332     {
15333         this.value = v;
15334         
15335         if(!this.ios_options){
15336             return;
15337         }
15338         
15339         Roo.each(this.ios_options, function(opts){
15340            
15341            opts.el.dom.removeAttribute('selected');
15342            
15343            if(opts.data[this.valueField] != v){
15344                return;
15345            }
15346            
15347            opts.el.dom.setAttribute('selected', true);
15348            
15349         }, this);
15350     }
15351
15352     /** 
15353     * @cfg {Boolean} grow 
15354     * @hide 
15355     */
15356     /** 
15357     * @cfg {Number} growMin 
15358     * @hide 
15359     */
15360     /** 
15361     * @cfg {Number} growMax 
15362     * @hide 
15363     */
15364     /**
15365      * @hide
15366      * @method autoSize
15367      */
15368 });
15369
15370 Roo.apply(Roo.bootstrap.ComboBox,  {
15371     
15372     header : {
15373         tag: 'div',
15374         cls: 'modal-header',
15375         cn: [
15376             {
15377                 tag: 'h4',
15378                 cls: 'modal-title'
15379             }
15380         ]
15381     },
15382     
15383     body : {
15384         tag: 'div',
15385         cls: 'modal-body',
15386         cn: [
15387             {
15388                 tag: 'ul',
15389                 cls: 'list-group'
15390             }
15391         ]
15392     },
15393     
15394     listItemRadio : {
15395         tag: 'li',
15396         cls: 'list-group-item',
15397         cn: [
15398             {
15399                 tag: 'span',
15400                 cls: 'roo-combobox-list-group-item-value'
15401             },
15402             {
15403                 tag: 'div',
15404                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15405                 cn: [
15406                     {
15407                         tag: 'input',
15408                         type: 'radio'
15409                     },
15410                     {
15411                         tag: 'label'
15412                     }
15413                 ]
15414             }
15415         ]
15416     },
15417     
15418     listItemCheckbox : {
15419         tag: 'li',
15420         cls: 'list-group-item',
15421         cn: [
15422             {
15423                 tag: 'span',
15424                 cls: 'roo-combobox-list-group-item-value'
15425             },
15426             {
15427                 tag: 'div',
15428                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15429                 cn: [
15430                     {
15431                         tag: 'input',
15432                         type: 'checkbox'
15433                     },
15434                     {
15435                         tag: 'label'
15436                     }
15437                 ]
15438             }
15439         ]
15440     },
15441     
15442     emptyResult : {
15443         tag: 'div',
15444         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15445     },
15446     
15447     footer : {
15448         tag: 'div',
15449         cls: 'modal-footer',
15450         cn: [
15451             {
15452                 tag: 'div',
15453                 cls: 'row',
15454                 cn: [
15455                     {
15456                         tag: 'div',
15457                         cls: 'col-xs-6 text-left',
15458                         cn: {
15459                             tag: 'button',
15460                             cls: 'btn btn-danger roo-touch-view-cancel',
15461                             html: 'Cancel'
15462                         }
15463                     },
15464                     {
15465                         tag: 'div',
15466                         cls: 'col-xs-6 text-right',
15467                         cn: {
15468                             tag: 'button',
15469                             cls: 'btn btn-success roo-touch-view-ok',
15470                             html: 'OK'
15471                         }
15472                     }
15473                 ]
15474             }
15475         ]
15476         
15477     }
15478 });
15479
15480 Roo.apply(Roo.bootstrap.ComboBox,  {
15481     
15482     touchViewTemplate : {
15483         tag: 'div',
15484         cls: 'modal fade roo-combobox-touch-view',
15485         cn: [
15486             {
15487                 tag: 'div',
15488                 cls: 'modal-dialog',
15489                 style : 'position:fixed', // we have to fix position....
15490                 cn: [
15491                     {
15492                         tag: 'div',
15493                         cls: 'modal-content',
15494                         cn: [
15495                             Roo.bootstrap.ComboBox.header,
15496                             Roo.bootstrap.ComboBox.body,
15497                             Roo.bootstrap.ComboBox.footer
15498                         ]
15499                     }
15500                 ]
15501             }
15502         ]
15503     }
15504 });/*
15505  * Based on:
15506  * Ext JS Library 1.1.1
15507  * Copyright(c) 2006-2007, Ext JS, LLC.
15508  *
15509  * Originally Released Under LGPL - original licence link has changed is not relivant.
15510  *
15511  * Fork - LGPL
15512  * <script type="text/javascript">
15513  */
15514
15515 /**
15516  * @class Roo.View
15517  * @extends Roo.util.Observable
15518  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15519  * This class also supports single and multi selection modes. <br>
15520  * Create a data model bound view:
15521  <pre><code>
15522  var store = new Roo.data.Store(...);
15523
15524  var view = new Roo.View({
15525     el : "my-element",
15526     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15527  
15528     singleSelect: true,
15529     selectedClass: "ydataview-selected",
15530     store: store
15531  });
15532
15533  // listen for node click?
15534  view.on("click", function(vw, index, node, e){
15535  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15536  });
15537
15538  // load XML data
15539  dataModel.load("foobar.xml");
15540  </code></pre>
15541  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15542  * <br><br>
15543  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15544  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15545  * 
15546  * Note: old style constructor is still suported (container, template, config)
15547  * 
15548  * @constructor
15549  * Create a new View
15550  * @param {Object} config The config object
15551  * 
15552  */
15553 Roo.View = function(config, depreciated_tpl, depreciated_config){
15554     
15555     this.parent = false;
15556     
15557     if (typeof(depreciated_tpl) == 'undefined') {
15558         // new way.. - universal constructor.
15559         Roo.apply(this, config);
15560         this.el  = Roo.get(this.el);
15561     } else {
15562         // old format..
15563         this.el  = Roo.get(config);
15564         this.tpl = depreciated_tpl;
15565         Roo.apply(this, depreciated_config);
15566     }
15567     this.wrapEl  = this.el.wrap().wrap();
15568     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15569     
15570     
15571     if(typeof(this.tpl) == "string"){
15572         this.tpl = new Roo.Template(this.tpl);
15573     } else {
15574         // support xtype ctors..
15575         this.tpl = new Roo.factory(this.tpl, Roo);
15576     }
15577     
15578     
15579     this.tpl.compile();
15580     
15581     /** @private */
15582     this.addEvents({
15583         /**
15584          * @event beforeclick
15585          * Fires before a click is processed. Returns false to cancel the default action.
15586          * @param {Roo.View} this
15587          * @param {Number} index The index of the target node
15588          * @param {HTMLElement} node The target node
15589          * @param {Roo.EventObject} e The raw event object
15590          */
15591             "beforeclick" : true,
15592         /**
15593          * @event click
15594          * Fires when a template node is clicked.
15595          * @param {Roo.View} this
15596          * @param {Number} index The index of the target node
15597          * @param {HTMLElement} node The target node
15598          * @param {Roo.EventObject} e The raw event object
15599          */
15600             "click" : true,
15601         /**
15602          * @event dblclick
15603          * Fires when a template node is double clicked.
15604          * @param {Roo.View} this
15605          * @param {Number} index The index of the target node
15606          * @param {HTMLElement} node The target node
15607          * @param {Roo.EventObject} e The raw event object
15608          */
15609             "dblclick" : true,
15610         /**
15611          * @event contextmenu
15612          * Fires when a template node is right clicked.
15613          * @param {Roo.View} this
15614          * @param {Number} index The index of the target node
15615          * @param {HTMLElement} node The target node
15616          * @param {Roo.EventObject} e The raw event object
15617          */
15618             "contextmenu" : true,
15619         /**
15620          * @event selectionchange
15621          * Fires when the selected nodes change.
15622          * @param {Roo.View} this
15623          * @param {Array} selections Array of the selected nodes
15624          */
15625             "selectionchange" : true,
15626     
15627         /**
15628          * @event beforeselect
15629          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15630          * @param {Roo.View} this
15631          * @param {HTMLElement} node The node to be selected
15632          * @param {Array} selections Array of currently selected nodes
15633          */
15634             "beforeselect" : true,
15635         /**
15636          * @event preparedata
15637          * Fires on every row to render, to allow you to change the data.
15638          * @param {Roo.View} this
15639          * @param {Object} data to be rendered (change this)
15640          */
15641           "preparedata" : true
15642           
15643           
15644         });
15645
15646
15647
15648     this.el.on({
15649         "click": this.onClick,
15650         "dblclick": this.onDblClick,
15651         "contextmenu": this.onContextMenu,
15652         scope:this
15653     });
15654
15655     this.selections = [];
15656     this.nodes = [];
15657     this.cmp = new Roo.CompositeElementLite([]);
15658     if(this.store){
15659         this.store = Roo.factory(this.store, Roo.data);
15660         this.setStore(this.store, true);
15661     }
15662     
15663     if ( this.footer && this.footer.xtype) {
15664            
15665          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15666         
15667         this.footer.dataSource = this.store;
15668         this.footer.container = fctr;
15669         this.footer = Roo.factory(this.footer, Roo);
15670         fctr.insertFirst(this.el);
15671         
15672         // this is a bit insane - as the paging toolbar seems to detach the el..
15673 //        dom.parentNode.parentNode.parentNode
15674          // they get detached?
15675     }
15676     
15677     
15678     Roo.View.superclass.constructor.call(this);
15679     
15680     
15681 };
15682
15683 Roo.extend(Roo.View, Roo.util.Observable, {
15684     
15685      /**
15686      * @cfg {Roo.data.Store} store Data store to load data from.
15687      */
15688     store : false,
15689     
15690     /**
15691      * @cfg {String|Roo.Element} el The container element.
15692      */
15693     el : '',
15694     
15695     /**
15696      * @cfg {String|Roo.Template} tpl The template used by this View 
15697      */
15698     tpl : false,
15699     /**
15700      * @cfg {String} dataName the named area of the template to use as the data area
15701      *                          Works with domtemplates roo-name="name"
15702      */
15703     dataName: false,
15704     /**
15705      * @cfg {String} selectedClass The css class to add to selected nodes
15706      */
15707     selectedClass : "x-view-selected",
15708      /**
15709      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15710      */
15711     emptyText : "",
15712     
15713     /**
15714      * @cfg {String} text to display on mask (default Loading)
15715      */
15716     mask : false,
15717     /**
15718      * @cfg {Boolean} multiSelect Allow multiple selection
15719      */
15720     multiSelect : false,
15721     /**
15722      * @cfg {Boolean} singleSelect Allow single selection
15723      */
15724     singleSelect:  false,
15725     
15726     /**
15727      * @cfg {Boolean} toggleSelect - selecting 
15728      */
15729     toggleSelect : false,
15730     
15731     /**
15732      * @cfg {Boolean} tickable - selecting 
15733      */
15734     tickable : false,
15735     
15736     /**
15737      * Returns the element this view is bound to.
15738      * @return {Roo.Element}
15739      */
15740     getEl : function(){
15741         return this.wrapEl;
15742     },
15743     
15744     
15745
15746     /**
15747      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15748      */
15749     refresh : function(){
15750         //Roo.log('refresh');
15751         var t = this.tpl;
15752         
15753         // if we are using something like 'domtemplate', then
15754         // the what gets used is:
15755         // t.applySubtemplate(NAME, data, wrapping data..)
15756         // the outer template then get' applied with
15757         //     the store 'extra data'
15758         // and the body get's added to the
15759         //      roo-name="data" node?
15760         //      <span class='roo-tpl-{name}'></span> ?????
15761         
15762         
15763         
15764         this.clearSelections();
15765         this.el.update("");
15766         var html = [];
15767         var records = this.store.getRange();
15768         if(records.length < 1) {
15769             
15770             // is this valid??  = should it render a template??
15771             
15772             this.el.update(this.emptyText);
15773             return;
15774         }
15775         var el = this.el;
15776         if (this.dataName) {
15777             this.el.update(t.apply(this.store.meta)); //????
15778             el = this.el.child('.roo-tpl-' + this.dataName);
15779         }
15780         
15781         for(var i = 0, len = records.length; i < len; i++){
15782             var data = this.prepareData(records[i].data, i, records[i]);
15783             this.fireEvent("preparedata", this, data, i, records[i]);
15784             
15785             var d = Roo.apply({}, data);
15786             
15787             if(this.tickable){
15788                 Roo.apply(d, {'roo-id' : Roo.id()});
15789                 
15790                 var _this = this;
15791             
15792                 Roo.each(this.parent.item, function(item){
15793                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15794                         return;
15795                     }
15796                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15797                 });
15798             }
15799             
15800             html[html.length] = Roo.util.Format.trim(
15801                 this.dataName ?
15802                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15803                     t.apply(d)
15804             );
15805         }
15806         
15807         
15808         
15809         el.update(html.join(""));
15810         this.nodes = el.dom.childNodes;
15811         this.updateIndexes(0);
15812     },
15813     
15814
15815     /**
15816      * Function to override to reformat the data that is sent to
15817      * the template for each node.
15818      * DEPRICATED - use the preparedata event handler.
15819      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15820      * a JSON object for an UpdateManager bound view).
15821      */
15822     prepareData : function(data, index, record)
15823     {
15824         this.fireEvent("preparedata", this, data, index, record);
15825         return data;
15826     },
15827
15828     onUpdate : function(ds, record){
15829         // Roo.log('on update');   
15830         this.clearSelections();
15831         var index = this.store.indexOf(record);
15832         var n = this.nodes[index];
15833         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15834         n.parentNode.removeChild(n);
15835         this.updateIndexes(index, index);
15836     },
15837
15838     
15839     
15840 // --------- FIXME     
15841     onAdd : function(ds, records, index)
15842     {
15843         //Roo.log(['on Add', ds, records, index] );        
15844         this.clearSelections();
15845         if(this.nodes.length == 0){
15846             this.refresh();
15847             return;
15848         }
15849         var n = this.nodes[index];
15850         for(var i = 0, len = records.length; i < len; i++){
15851             var d = this.prepareData(records[i].data, i, records[i]);
15852             if(n){
15853                 this.tpl.insertBefore(n, d);
15854             }else{
15855                 
15856                 this.tpl.append(this.el, d);
15857             }
15858         }
15859         this.updateIndexes(index);
15860     },
15861
15862     onRemove : function(ds, record, index){
15863        // Roo.log('onRemove');
15864         this.clearSelections();
15865         var el = this.dataName  ?
15866             this.el.child('.roo-tpl-' + this.dataName) :
15867             this.el; 
15868         
15869         el.dom.removeChild(this.nodes[index]);
15870         this.updateIndexes(index);
15871     },
15872
15873     /**
15874      * Refresh an individual node.
15875      * @param {Number} index
15876      */
15877     refreshNode : function(index){
15878         this.onUpdate(this.store, this.store.getAt(index));
15879     },
15880
15881     updateIndexes : function(startIndex, endIndex){
15882         var ns = this.nodes;
15883         startIndex = startIndex || 0;
15884         endIndex = endIndex || ns.length - 1;
15885         for(var i = startIndex; i <= endIndex; i++){
15886             ns[i].nodeIndex = i;
15887         }
15888     },
15889
15890     /**
15891      * Changes the data store this view uses and refresh the view.
15892      * @param {Store} store
15893      */
15894     setStore : function(store, initial){
15895         if(!initial && this.store){
15896             this.store.un("datachanged", this.refresh);
15897             this.store.un("add", this.onAdd);
15898             this.store.un("remove", this.onRemove);
15899             this.store.un("update", this.onUpdate);
15900             this.store.un("clear", this.refresh);
15901             this.store.un("beforeload", this.onBeforeLoad);
15902             this.store.un("load", this.onLoad);
15903             this.store.un("loadexception", this.onLoad);
15904         }
15905         if(store){
15906           
15907             store.on("datachanged", this.refresh, this);
15908             store.on("add", this.onAdd, this);
15909             store.on("remove", this.onRemove, this);
15910             store.on("update", this.onUpdate, this);
15911             store.on("clear", this.refresh, this);
15912             store.on("beforeload", this.onBeforeLoad, this);
15913             store.on("load", this.onLoad, this);
15914             store.on("loadexception", this.onLoad, this);
15915         }
15916         
15917         if(store){
15918             this.refresh();
15919         }
15920     },
15921     /**
15922      * onbeforeLoad - masks the loading area.
15923      *
15924      */
15925     onBeforeLoad : function(store,opts)
15926     {
15927          //Roo.log('onBeforeLoad');   
15928         if (!opts.add) {
15929             this.el.update("");
15930         }
15931         this.el.mask(this.mask ? this.mask : "Loading" ); 
15932     },
15933     onLoad : function ()
15934     {
15935         this.el.unmask();
15936     },
15937     
15938
15939     /**
15940      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15941      * @param {HTMLElement} node
15942      * @return {HTMLElement} The template node
15943      */
15944     findItemFromChild : function(node){
15945         var el = this.dataName  ?
15946             this.el.child('.roo-tpl-' + this.dataName,true) :
15947             this.el.dom; 
15948         
15949         if(!node || node.parentNode == el){
15950                     return node;
15951             }
15952             var p = node.parentNode;
15953             while(p && p != el){
15954             if(p.parentNode == el){
15955                 return p;
15956             }
15957             p = p.parentNode;
15958         }
15959             return null;
15960     },
15961
15962     /** @ignore */
15963     onClick : function(e){
15964         var item = this.findItemFromChild(e.getTarget());
15965         if(item){
15966             var index = this.indexOf(item);
15967             if(this.onItemClick(item, index, e) !== false){
15968                 this.fireEvent("click", this, index, item, e);
15969             }
15970         }else{
15971             this.clearSelections();
15972         }
15973     },
15974
15975     /** @ignore */
15976     onContextMenu : function(e){
15977         var item = this.findItemFromChild(e.getTarget());
15978         if(item){
15979             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15980         }
15981     },
15982
15983     /** @ignore */
15984     onDblClick : function(e){
15985         var item = this.findItemFromChild(e.getTarget());
15986         if(item){
15987             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15988         }
15989     },
15990
15991     onItemClick : function(item, index, e)
15992     {
15993         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15994             return false;
15995         }
15996         if (this.toggleSelect) {
15997             var m = this.isSelected(item) ? 'unselect' : 'select';
15998             //Roo.log(m);
15999             var _t = this;
16000             _t[m](item, true, false);
16001             return true;
16002         }
16003         if(this.multiSelect || this.singleSelect){
16004             if(this.multiSelect && e.shiftKey && this.lastSelection){
16005                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16006             }else{
16007                 this.select(item, this.multiSelect && e.ctrlKey);
16008                 this.lastSelection = item;
16009             }
16010             
16011             if(!this.tickable){
16012                 e.preventDefault();
16013             }
16014             
16015         }
16016         return true;
16017     },
16018
16019     /**
16020      * Get the number of selected nodes.
16021      * @return {Number}
16022      */
16023     getSelectionCount : function(){
16024         return this.selections.length;
16025     },
16026
16027     /**
16028      * Get the currently selected nodes.
16029      * @return {Array} An array of HTMLElements
16030      */
16031     getSelectedNodes : function(){
16032         return this.selections;
16033     },
16034
16035     /**
16036      * Get the indexes of the selected nodes.
16037      * @return {Array}
16038      */
16039     getSelectedIndexes : function(){
16040         var indexes = [], s = this.selections;
16041         for(var i = 0, len = s.length; i < len; i++){
16042             indexes.push(s[i].nodeIndex);
16043         }
16044         return indexes;
16045     },
16046
16047     /**
16048      * Clear all selections
16049      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16050      */
16051     clearSelections : function(suppressEvent){
16052         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16053             this.cmp.elements = this.selections;
16054             this.cmp.removeClass(this.selectedClass);
16055             this.selections = [];
16056             if(!suppressEvent){
16057                 this.fireEvent("selectionchange", this, this.selections);
16058             }
16059         }
16060     },
16061
16062     /**
16063      * Returns true if the passed node is selected
16064      * @param {HTMLElement/Number} node The node or node index
16065      * @return {Boolean}
16066      */
16067     isSelected : function(node){
16068         var s = this.selections;
16069         if(s.length < 1){
16070             return false;
16071         }
16072         node = this.getNode(node);
16073         return s.indexOf(node) !== -1;
16074     },
16075
16076     /**
16077      * Selects nodes.
16078      * @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
16079      * @param {Boolean} keepExisting (optional) true to keep existing selections
16080      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16081      */
16082     select : function(nodeInfo, keepExisting, suppressEvent){
16083         if(nodeInfo instanceof Array){
16084             if(!keepExisting){
16085                 this.clearSelections(true);
16086             }
16087             for(var i = 0, len = nodeInfo.length; i < len; i++){
16088                 this.select(nodeInfo[i], true, true);
16089             }
16090             return;
16091         } 
16092         var node = this.getNode(nodeInfo);
16093         if(!node || this.isSelected(node)){
16094             return; // already selected.
16095         }
16096         if(!keepExisting){
16097             this.clearSelections(true);
16098         }
16099         
16100         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16101             Roo.fly(node).addClass(this.selectedClass);
16102             this.selections.push(node);
16103             if(!suppressEvent){
16104                 this.fireEvent("selectionchange", this, this.selections);
16105             }
16106         }
16107         
16108         
16109     },
16110       /**
16111      * Unselects nodes.
16112      * @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
16113      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16114      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16115      */
16116     unselect : function(nodeInfo, keepExisting, suppressEvent)
16117     {
16118         if(nodeInfo instanceof Array){
16119             Roo.each(this.selections, function(s) {
16120                 this.unselect(s, nodeInfo);
16121             }, this);
16122             return;
16123         }
16124         var node = this.getNode(nodeInfo);
16125         if(!node || !this.isSelected(node)){
16126             //Roo.log("not selected");
16127             return; // not selected.
16128         }
16129         // fireevent???
16130         var ns = [];
16131         Roo.each(this.selections, function(s) {
16132             if (s == node ) {
16133                 Roo.fly(node).removeClass(this.selectedClass);
16134
16135                 return;
16136             }
16137             ns.push(s);
16138         },this);
16139         
16140         this.selections= ns;
16141         this.fireEvent("selectionchange", this, this.selections);
16142     },
16143
16144     /**
16145      * Gets a template node.
16146      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16147      * @return {HTMLElement} The node or null if it wasn't found
16148      */
16149     getNode : function(nodeInfo){
16150         if(typeof nodeInfo == "string"){
16151             return document.getElementById(nodeInfo);
16152         }else if(typeof nodeInfo == "number"){
16153             return this.nodes[nodeInfo];
16154         }
16155         return nodeInfo;
16156     },
16157
16158     /**
16159      * Gets a range template nodes.
16160      * @param {Number} startIndex
16161      * @param {Number} endIndex
16162      * @return {Array} An array of nodes
16163      */
16164     getNodes : function(start, end){
16165         var ns = this.nodes;
16166         start = start || 0;
16167         end = typeof end == "undefined" ? ns.length - 1 : end;
16168         var nodes = [];
16169         if(start <= end){
16170             for(var i = start; i <= end; i++){
16171                 nodes.push(ns[i]);
16172             }
16173         } else{
16174             for(var i = start; i >= end; i--){
16175                 nodes.push(ns[i]);
16176             }
16177         }
16178         return nodes;
16179     },
16180
16181     /**
16182      * Finds the index of the passed node
16183      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16184      * @return {Number} The index of the node or -1
16185      */
16186     indexOf : function(node){
16187         node = this.getNode(node);
16188         if(typeof node.nodeIndex == "number"){
16189             return node.nodeIndex;
16190         }
16191         var ns = this.nodes;
16192         for(var i = 0, len = ns.length; i < len; i++){
16193             if(ns[i] == node){
16194                 return i;
16195             }
16196         }
16197         return -1;
16198     }
16199 });
16200 /*
16201  * - LGPL
16202  *
16203  * based on jquery fullcalendar
16204  * 
16205  */
16206
16207 Roo.bootstrap = Roo.bootstrap || {};
16208 /**
16209  * @class Roo.bootstrap.Calendar
16210  * @extends Roo.bootstrap.Component
16211  * Bootstrap Calendar class
16212  * @cfg {Boolean} loadMask (true|false) default false
16213  * @cfg {Object} header generate the user specific header of the calendar, default false
16214
16215  * @constructor
16216  * Create a new Container
16217  * @param {Object} config The config object
16218  */
16219
16220
16221
16222 Roo.bootstrap.Calendar = function(config){
16223     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16224      this.addEvents({
16225         /**
16226              * @event select
16227              * Fires when a date is selected
16228              * @param {DatePicker} this
16229              * @param {Date} date The selected date
16230              */
16231         'select': true,
16232         /**
16233              * @event monthchange
16234              * Fires when the displayed month changes 
16235              * @param {DatePicker} this
16236              * @param {Date} date The selected month
16237              */
16238         'monthchange': true,
16239         /**
16240              * @event evententer
16241              * Fires when mouse over an event
16242              * @param {Calendar} this
16243              * @param {event} Event
16244              */
16245         'evententer': true,
16246         /**
16247              * @event eventleave
16248              * Fires when the mouse leaves an
16249              * @param {Calendar} this
16250              * @param {event}
16251              */
16252         'eventleave': true,
16253         /**
16254              * @event eventclick
16255              * Fires when the mouse click an
16256              * @param {Calendar} this
16257              * @param {event}
16258              */
16259         'eventclick': true
16260         
16261     });
16262
16263 };
16264
16265 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16266     
16267      /**
16268      * @cfg {Number} startDay
16269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16270      */
16271     startDay : 0,
16272     
16273     loadMask : false,
16274     
16275     header : false,
16276       
16277     getAutoCreate : function(){
16278         
16279         
16280         var fc_button = function(name, corner, style, content ) {
16281             return Roo.apply({},{
16282                 tag : 'span',
16283                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16284                          (corner.length ?
16285                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16286                             ''
16287                         ),
16288                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16289                 unselectable: 'on'
16290             });
16291         };
16292         
16293         var header = {};
16294         
16295         if(!this.header){
16296             header = {
16297                 tag : 'table',
16298                 cls : 'fc-header',
16299                 style : 'width:100%',
16300                 cn : [
16301                     {
16302                         tag: 'tr',
16303                         cn : [
16304                             {
16305                                 tag : 'td',
16306                                 cls : 'fc-header-left',
16307                                 cn : [
16308                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16309                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16310                                     { tag: 'span', cls: 'fc-header-space' },
16311                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16312
16313
16314                                 ]
16315                             },
16316
16317                             {
16318                                 tag : 'td',
16319                                 cls : 'fc-header-center',
16320                                 cn : [
16321                                     {
16322                                         tag: 'span',
16323                                         cls: 'fc-header-title',
16324                                         cn : {
16325                                             tag: 'H2',
16326                                             html : 'month / year'
16327                                         }
16328                                     }
16329
16330                                 ]
16331                             },
16332                             {
16333                                 tag : 'td',
16334                                 cls : 'fc-header-right',
16335                                 cn : [
16336                               /*      fc_button('month', 'left', '', 'month' ),
16337                                     fc_button('week', '', '', 'week' ),
16338                                     fc_button('day', 'right', '', 'day' )
16339                                 */    
16340
16341                                 ]
16342                             }
16343
16344                         ]
16345                     }
16346                 ]
16347             };
16348         }
16349         
16350         header = this.header;
16351         
16352        
16353         var cal_heads = function() {
16354             var ret = [];
16355             // fixme - handle this.
16356             
16357             for (var i =0; i < Date.dayNames.length; i++) {
16358                 var d = Date.dayNames[i];
16359                 ret.push({
16360                     tag: 'th',
16361                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16362                     html : d.substring(0,3)
16363                 });
16364                 
16365             }
16366             ret[0].cls += ' fc-first';
16367             ret[6].cls += ' fc-last';
16368             return ret;
16369         };
16370         var cal_cell = function(n) {
16371             return  {
16372                 tag: 'td',
16373                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16374                 cn : [
16375                     {
16376                         cn : [
16377                             {
16378                                 cls: 'fc-day-number',
16379                                 html: 'D'
16380                             },
16381                             {
16382                                 cls: 'fc-day-content',
16383                              
16384                                 cn : [
16385                                      {
16386                                         style: 'position: relative;' // height: 17px;
16387                                     }
16388                                 ]
16389                             }
16390                             
16391                             
16392                         ]
16393                     }
16394                 ]
16395                 
16396             }
16397         };
16398         var cal_rows = function() {
16399             
16400             var ret = [];
16401             for (var r = 0; r < 6; r++) {
16402                 var row= {
16403                     tag : 'tr',
16404                     cls : 'fc-week',
16405                     cn : []
16406                 };
16407                 
16408                 for (var i =0; i < Date.dayNames.length; i++) {
16409                     var d = Date.dayNames[i];
16410                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16411
16412                 }
16413                 row.cn[0].cls+=' fc-first';
16414                 row.cn[0].cn[0].style = 'min-height:90px';
16415                 row.cn[6].cls+=' fc-last';
16416                 ret.push(row);
16417                 
16418             }
16419             ret[0].cls += ' fc-first';
16420             ret[4].cls += ' fc-prev-last';
16421             ret[5].cls += ' fc-last';
16422             return ret;
16423             
16424         };
16425         
16426         var cal_table = {
16427             tag: 'table',
16428             cls: 'fc-border-separate',
16429             style : 'width:100%',
16430             cellspacing  : 0,
16431             cn : [
16432                 { 
16433                     tag: 'thead',
16434                     cn : [
16435                         { 
16436                             tag: 'tr',
16437                             cls : 'fc-first fc-last',
16438                             cn : cal_heads()
16439                         }
16440                     ]
16441                 },
16442                 { 
16443                     tag: 'tbody',
16444                     cn : cal_rows()
16445                 }
16446                   
16447             ]
16448         };
16449          
16450          var cfg = {
16451             cls : 'fc fc-ltr',
16452             cn : [
16453                 header,
16454                 {
16455                     cls : 'fc-content',
16456                     style : "position: relative;",
16457                     cn : [
16458                         {
16459                             cls : 'fc-view fc-view-month fc-grid',
16460                             style : 'position: relative',
16461                             unselectable : 'on',
16462                             cn : [
16463                                 {
16464                                     cls : 'fc-event-container',
16465                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16466                                 },
16467                                 cal_table
16468                             ]
16469                         }
16470                     ]
16471     
16472                 }
16473            ] 
16474             
16475         };
16476         
16477          
16478         
16479         return cfg;
16480     },
16481     
16482     
16483     initEvents : function()
16484     {
16485         if(!this.store){
16486             throw "can not find store for calendar";
16487         }
16488         
16489         var mark = {
16490             tag: "div",
16491             cls:"x-dlg-mask",
16492             style: "text-align:center",
16493             cn: [
16494                 {
16495                     tag: "div",
16496                     style: "background-color:white;width:50%;margin:250 auto",
16497                     cn: [
16498                         {
16499                             tag: "img",
16500                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16501                         },
16502                         {
16503                             tag: "span",
16504                             html: "Loading"
16505                         }
16506                         
16507                     ]
16508                 }
16509             ]
16510         };
16511         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16512         
16513         var size = this.el.select('.fc-content', true).first().getSize();
16514         this.maskEl.setSize(size.width, size.height);
16515         this.maskEl.enableDisplayMode("block");
16516         if(!this.loadMask){
16517             this.maskEl.hide();
16518         }
16519         
16520         this.store = Roo.factory(this.store, Roo.data);
16521         this.store.on('load', this.onLoad, this);
16522         this.store.on('beforeload', this.onBeforeLoad, this);
16523         
16524         this.resize();
16525         
16526         this.cells = this.el.select('.fc-day',true);
16527         //Roo.log(this.cells);
16528         this.textNodes = this.el.query('.fc-day-number');
16529         this.cells.addClassOnOver('fc-state-hover');
16530         
16531         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16532         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16533         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16534         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16535         
16536         this.on('monthchange', this.onMonthChange, this);
16537         
16538         this.update(new Date().clearTime());
16539     },
16540     
16541     resize : function() {
16542         var sz  = this.el.getSize();
16543         
16544         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16545         this.el.select('.fc-day-content div',true).setHeight(34);
16546     },
16547     
16548     
16549     // private
16550     showPrevMonth : function(e){
16551         this.update(this.activeDate.add("mo", -1));
16552     },
16553     showToday : function(e){
16554         this.update(new Date().clearTime());
16555     },
16556     // private
16557     showNextMonth : function(e){
16558         this.update(this.activeDate.add("mo", 1));
16559     },
16560
16561     // private
16562     showPrevYear : function(){
16563         this.update(this.activeDate.add("y", -1));
16564     },
16565
16566     // private
16567     showNextYear : function(){
16568         this.update(this.activeDate.add("y", 1));
16569     },
16570
16571     
16572    // private
16573     update : function(date)
16574     {
16575         var vd = this.activeDate;
16576         this.activeDate = date;
16577 //        if(vd && this.el){
16578 //            var t = date.getTime();
16579 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16580 //                Roo.log('using add remove');
16581 //                
16582 //                this.fireEvent('monthchange', this, date);
16583 //                
16584 //                this.cells.removeClass("fc-state-highlight");
16585 //                this.cells.each(function(c){
16586 //                   if(c.dateValue == t){
16587 //                       c.addClass("fc-state-highlight");
16588 //                       setTimeout(function(){
16589 //                            try{c.dom.firstChild.focus();}catch(e){}
16590 //                       }, 50);
16591 //                       return false;
16592 //                   }
16593 //                   return true;
16594 //                });
16595 //                return;
16596 //            }
16597 //        }
16598         
16599         var days = date.getDaysInMonth();
16600         
16601         var firstOfMonth = date.getFirstDateOfMonth();
16602         var startingPos = firstOfMonth.getDay()-this.startDay;
16603         
16604         if(startingPos < this.startDay){
16605             startingPos += 7;
16606         }
16607         
16608         var pm = date.add(Date.MONTH, -1);
16609         var prevStart = pm.getDaysInMonth()-startingPos;
16610 //        
16611         this.cells = this.el.select('.fc-day',true);
16612         this.textNodes = this.el.query('.fc-day-number');
16613         this.cells.addClassOnOver('fc-state-hover');
16614         
16615         var cells = this.cells.elements;
16616         var textEls = this.textNodes;
16617         
16618         Roo.each(cells, function(cell){
16619             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16620         });
16621         
16622         days += startingPos;
16623
16624         // convert everything to numbers so it's fast
16625         var day = 86400000;
16626         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16627         //Roo.log(d);
16628         //Roo.log(pm);
16629         //Roo.log(prevStart);
16630         
16631         var today = new Date().clearTime().getTime();
16632         var sel = date.clearTime().getTime();
16633         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16634         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16635         var ddMatch = this.disabledDatesRE;
16636         var ddText = this.disabledDatesText;
16637         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16638         var ddaysText = this.disabledDaysText;
16639         var format = this.format;
16640         
16641         var setCellClass = function(cal, cell){
16642             cell.row = 0;
16643             cell.events = [];
16644             cell.more = [];
16645             //Roo.log('set Cell Class');
16646             cell.title = "";
16647             var t = d.getTime();
16648             
16649             //Roo.log(d);
16650             
16651             cell.dateValue = t;
16652             if(t == today){
16653                 cell.className += " fc-today";
16654                 cell.className += " fc-state-highlight";
16655                 cell.title = cal.todayText;
16656             }
16657             if(t == sel){
16658                 // disable highlight in other month..
16659                 //cell.className += " fc-state-highlight";
16660                 
16661             }
16662             // disabling
16663             if(t < min) {
16664                 cell.className = " fc-state-disabled";
16665                 cell.title = cal.minText;
16666                 return;
16667             }
16668             if(t > max) {
16669                 cell.className = " fc-state-disabled";
16670                 cell.title = cal.maxText;
16671                 return;
16672             }
16673             if(ddays){
16674                 if(ddays.indexOf(d.getDay()) != -1){
16675                     cell.title = ddaysText;
16676                     cell.className = " fc-state-disabled";
16677                 }
16678             }
16679             if(ddMatch && format){
16680                 var fvalue = d.dateFormat(format);
16681                 if(ddMatch.test(fvalue)){
16682                     cell.title = ddText.replace("%0", fvalue);
16683                     cell.className = " fc-state-disabled";
16684                 }
16685             }
16686             
16687             if (!cell.initialClassName) {
16688                 cell.initialClassName = cell.dom.className;
16689             }
16690             
16691             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16692         };
16693
16694         var i = 0;
16695         
16696         for(; i < startingPos; i++) {
16697             textEls[i].innerHTML = (++prevStart);
16698             d.setDate(d.getDate()+1);
16699             
16700             cells[i].className = "fc-past fc-other-month";
16701             setCellClass(this, cells[i]);
16702         }
16703         
16704         var intDay = 0;
16705         
16706         for(; i < days; i++){
16707             intDay = i - startingPos + 1;
16708             textEls[i].innerHTML = (intDay);
16709             d.setDate(d.getDate()+1);
16710             
16711             cells[i].className = ''; // "x-date-active";
16712             setCellClass(this, cells[i]);
16713         }
16714         var extraDays = 0;
16715         
16716         for(; i < 42; i++) {
16717             textEls[i].innerHTML = (++extraDays);
16718             d.setDate(d.getDate()+1);
16719             
16720             cells[i].className = "fc-future fc-other-month";
16721             setCellClass(this, cells[i]);
16722         }
16723         
16724         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16725         
16726         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16727         
16728         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16729         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16730         
16731         if(totalRows != 6){
16732             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16733             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16734         }
16735         
16736         this.fireEvent('monthchange', this, date);
16737         
16738         
16739         /*
16740         if(!this.internalRender){
16741             var main = this.el.dom.firstChild;
16742             var w = main.offsetWidth;
16743             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16744             Roo.fly(main).setWidth(w);
16745             this.internalRender = true;
16746             // opera does not respect the auto grow header center column
16747             // then, after it gets a width opera refuses to recalculate
16748             // without a second pass
16749             if(Roo.isOpera && !this.secondPass){
16750                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16751                 this.secondPass = true;
16752                 this.update.defer(10, this, [date]);
16753             }
16754         }
16755         */
16756         
16757     },
16758     
16759     findCell : function(dt) {
16760         dt = dt.clearTime().getTime();
16761         var ret = false;
16762         this.cells.each(function(c){
16763             //Roo.log("check " +c.dateValue + '?=' + dt);
16764             if(c.dateValue == dt){
16765                 ret = c;
16766                 return false;
16767             }
16768             return true;
16769         });
16770         
16771         return ret;
16772     },
16773     
16774     findCells : function(ev) {
16775         var s = ev.start.clone().clearTime().getTime();
16776        // Roo.log(s);
16777         var e= ev.end.clone().clearTime().getTime();
16778        // Roo.log(e);
16779         var ret = [];
16780         this.cells.each(function(c){
16781              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16782             
16783             if(c.dateValue > e){
16784                 return ;
16785             }
16786             if(c.dateValue < s){
16787                 return ;
16788             }
16789             ret.push(c);
16790         });
16791         
16792         return ret;    
16793     },
16794     
16795 //    findBestRow: function(cells)
16796 //    {
16797 //        var ret = 0;
16798 //        
16799 //        for (var i =0 ; i < cells.length;i++) {
16800 //            ret  = Math.max(cells[i].rows || 0,ret);
16801 //        }
16802 //        return ret;
16803 //        
16804 //    },
16805     
16806     
16807     addItem : function(ev)
16808     {
16809         // look for vertical location slot in
16810         var cells = this.findCells(ev);
16811         
16812 //        ev.row = this.findBestRow(cells);
16813         
16814         // work out the location.
16815         
16816         var crow = false;
16817         var rows = [];
16818         for(var i =0; i < cells.length; i++) {
16819             
16820             cells[i].row = cells[0].row;
16821             
16822             if(i == 0){
16823                 cells[i].row = cells[i].row + 1;
16824             }
16825             
16826             if (!crow) {
16827                 crow = {
16828                     start : cells[i],
16829                     end :  cells[i]
16830                 };
16831                 continue;
16832             }
16833             if (crow.start.getY() == cells[i].getY()) {
16834                 // on same row.
16835                 crow.end = cells[i];
16836                 continue;
16837             }
16838             // different row.
16839             rows.push(crow);
16840             crow = {
16841                 start: cells[i],
16842                 end : cells[i]
16843             };
16844             
16845         }
16846         
16847         rows.push(crow);
16848         ev.els = [];
16849         ev.rows = rows;
16850         ev.cells = cells;
16851         
16852         cells[0].events.push(ev);
16853         
16854         this.calevents.push(ev);
16855     },
16856     
16857     clearEvents: function() {
16858         
16859         if(!this.calevents){
16860             return;
16861         }
16862         
16863         Roo.each(this.cells.elements, function(c){
16864             c.row = 0;
16865             c.events = [];
16866             c.more = [];
16867         });
16868         
16869         Roo.each(this.calevents, function(e) {
16870             Roo.each(e.els, function(el) {
16871                 el.un('mouseenter' ,this.onEventEnter, this);
16872                 el.un('mouseleave' ,this.onEventLeave, this);
16873                 el.remove();
16874             },this);
16875         },this);
16876         
16877         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16878             e.remove();
16879         });
16880         
16881     },
16882     
16883     renderEvents: function()
16884     {   
16885         var _this = this;
16886         
16887         this.cells.each(function(c) {
16888             
16889             if(c.row < 5){
16890                 return;
16891             }
16892             
16893             var ev = c.events;
16894             
16895             var r = 4;
16896             if(c.row != c.events.length){
16897                 r = 4 - (4 - (c.row - c.events.length));
16898             }
16899             
16900             c.events = ev.slice(0, r);
16901             c.more = ev.slice(r);
16902             
16903             if(c.more.length && c.more.length == 1){
16904                 c.events.push(c.more.pop());
16905             }
16906             
16907             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16908             
16909         });
16910             
16911         this.cells.each(function(c) {
16912             
16913             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16914             
16915             
16916             for (var e = 0; e < c.events.length; e++){
16917                 var ev = c.events[e];
16918                 var rows = ev.rows;
16919                 
16920                 for(var i = 0; i < rows.length; i++) {
16921                 
16922                     // how many rows should it span..
16923
16924                     var  cfg = {
16925                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16926                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16927
16928                         unselectable : "on",
16929                         cn : [
16930                             {
16931                                 cls: 'fc-event-inner',
16932                                 cn : [
16933     //                                {
16934     //                                  tag:'span',
16935     //                                  cls: 'fc-event-time',
16936     //                                  html : cells.length > 1 ? '' : ev.time
16937     //                                },
16938                                     {
16939                                       tag:'span',
16940                                       cls: 'fc-event-title',
16941                                       html : String.format('{0}', ev.title)
16942                                     }
16943
16944
16945                                 ]
16946                             },
16947                             {
16948                                 cls: 'ui-resizable-handle ui-resizable-e',
16949                                 html : '&nbsp;&nbsp;&nbsp'
16950                             }
16951
16952                         ]
16953                     };
16954
16955                     if (i == 0) {
16956                         cfg.cls += ' fc-event-start';
16957                     }
16958                     if ((i+1) == rows.length) {
16959                         cfg.cls += ' fc-event-end';
16960                     }
16961
16962                     var ctr = _this.el.select('.fc-event-container',true).first();
16963                     var cg = ctr.createChild(cfg);
16964
16965                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16966                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16967
16968                     var r = (c.more.length) ? 1 : 0;
16969                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16970                     cg.setWidth(ebox.right - sbox.x -2);
16971
16972                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16973                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16974                     cg.on('click', _this.onEventClick, _this, ev);
16975
16976                     ev.els.push(cg);
16977                     
16978                 }
16979                 
16980             }
16981             
16982             
16983             if(c.more.length){
16984                 var  cfg = {
16985                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16986                     style : 'position: absolute',
16987                     unselectable : "on",
16988                     cn : [
16989                         {
16990                             cls: 'fc-event-inner',
16991                             cn : [
16992                                 {
16993                                   tag:'span',
16994                                   cls: 'fc-event-title',
16995                                   html : 'More'
16996                                 }
16997
16998
16999                             ]
17000                         },
17001                         {
17002                             cls: 'ui-resizable-handle ui-resizable-e',
17003                             html : '&nbsp;&nbsp;&nbsp'
17004                         }
17005
17006                     ]
17007                 };
17008
17009                 var ctr = _this.el.select('.fc-event-container',true).first();
17010                 var cg = ctr.createChild(cfg);
17011
17012                 var sbox = c.select('.fc-day-content',true).first().getBox();
17013                 var ebox = c.select('.fc-day-content',true).first().getBox();
17014                 //Roo.log(cg);
17015                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17016                 cg.setWidth(ebox.right - sbox.x -2);
17017
17018                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17019                 
17020             }
17021             
17022         });
17023         
17024         
17025         
17026     },
17027     
17028     onEventEnter: function (e, el,event,d) {
17029         this.fireEvent('evententer', this, el, event);
17030     },
17031     
17032     onEventLeave: function (e, el,event,d) {
17033         this.fireEvent('eventleave', this, el, event);
17034     },
17035     
17036     onEventClick: function (e, el,event,d) {
17037         this.fireEvent('eventclick', this, el, event);
17038     },
17039     
17040     onMonthChange: function () {
17041         this.store.load();
17042     },
17043     
17044     onMoreEventClick: function(e, el, more)
17045     {
17046         var _this = this;
17047         
17048         this.calpopover.placement = 'right';
17049         this.calpopover.setTitle('More');
17050         
17051         this.calpopover.setContent('');
17052         
17053         var ctr = this.calpopover.el.select('.popover-content', true).first();
17054         
17055         Roo.each(more, function(m){
17056             var cfg = {
17057                 cls : 'fc-event-hori fc-event-draggable',
17058                 html : m.title
17059             };
17060             var cg = ctr.createChild(cfg);
17061             
17062             cg.on('click', _this.onEventClick, _this, m);
17063         });
17064         
17065         this.calpopover.show(el);
17066         
17067         
17068     },
17069     
17070     onLoad: function () 
17071     {   
17072         this.calevents = [];
17073         var cal = this;
17074         
17075         if(this.store.getCount() > 0){
17076             this.store.data.each(function(d){
17077                cal.addItem({
17078                     id : d.data.id,
17079                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17080                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17081                     time : d.data.start_time,
17082                     title : d.data.title,
17083                     description : d.data.description,
17084                     venue : d.data.venue
17085                 });
17086             });
17087         }
17088         
17089         this.renderEvents();
17090         
17091         if(this.calevents.length && this.loadMask){
17092             this.maskEl.hide();
17093         }
17094     },
17095     
17096     onBeforeLoad: function()
17097     {
17098         this.clearEvents();
17099         if(this.loadMask){
17100             this.maskEl.show();
17101         }
17102     }
17103 });
17104
17105  
17106  /*
17107  * - LGPL
17108  *
17109  * element
17110  * 
17111  */
17112
17113 /**
17114  * @class Roo.bootstrap.Popover
17115  * @extends Roo.bootstrap.Component
17116  * Bootstrap Popover class
17117  * @cfg {String} html contents of the popover   (or false to use children..)
17118  * @cfg {String} title of popover (or false to hide)
17119  * @cfg {String} placement how it is placed
17120  * @cfg {String} trigger click || hover (or false to trigger manually)
17121  * @cfg {String} over what (parent or false to trigger manually.)
17122  * @cfg {Number} delay - delay before showing
17123  
17124  * @constructor
17125  * Create a new Popover
17126  * @param {Object} config The config object
17127  */
17128
17129 Roo.bootstrap.Popover = function(config){
17130     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17131     
17132     this.addEvents({
17133         // raw events
17134          /**
17135          * @event show
17136          * After the popover show
17137          * 
17138          * @param {Roo.bootstrap.Popover} this
17139          */
17140         "show" : true,
17141         /**
17142          * @event hide
17143          * After the popover hide
17144          * 
17145          * @param {Roo.bootstrap.Popover} this
17146          */
17147         "hide" : true
17148     });
17149 };
17150
17151 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17152     
17153     title: 'Fill in a title',
17154     html: false,
17155     
17156     placement : 'right',
17157     trigger : 'hover', // hover
17158     
17159     delay : 0,
17160     
17161     over: 'parent',
17162     
17163     can_build_overlaid : false,
17164     
17165     getChildContainer : function()
17166     {
17167         return this.el.select('.popover-content',true).first();
17168     },
17169     
17170     getAutoCreate : function(){
17171          
17172         var cfg = {
17173            cls : 'popover roo-dynamic',
17174            style: 'display:block',
17175            cn : [
17176                 {
17177                     cls : 'arrow'
17178                 },
17179                 {
17180                     cls : 'popover-inner',
17181                     cn : [
17182                         {
17183                             tag: 'h3',
17184                             cls: 'popover-title',
17185                             html : this.title
17186                         },
17187                         {
17188                             cls : 'popover-content',
17189                             html : this.html
17190                         }
17191                     ]
17192                     
17193                 }
17194            ]
17195         };
17196         
17197         return cfg;
17198     },
17199     setTitle: function(str)
17200     {
17201         this.title = str;
17202         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17203     },
17204     setContent: function(str)
17205     {
17206         this.html = str;
17207         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17208     },
17209     // as it get's added to the bottom of the page.
17210     onRender : function(ct, position)
17211     {
17212         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17213         if(!this.el){
17214             var cfg = Roo.apply({},  this.getAutoCreate());
17215             cfg.id = Roo.id();
17216             
17217             if (this.cls) {
17218                 cfg.cls += ' ' + this.cls;
17219             }
17220             if (this.style) {
17221                 cfg.style = this.style;
17222             }
17223             //Roo.log("adding to ");
17224             this.el = Roo.get(document.body).createChild(cfg, position);
17225 //            Roo.log(this.el);
17226         }
17227         this.initEvents();
17228     },
17229     
17230     initEvents : function()
17231     {
17232         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17233         this.el.enableDisplayMode('block');
17234         this.el.hide();
17235         if (this.over === false) {
17236             return; 
17237         }
17238         if (this.triggers === false) {
17239             return;
17240         }
17241         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17242         var triggers = this.trigger ? this.trigger.split(' ') : [];
17243         Roo.each(triggers, function(trigger) {
17244         
17245             if (trigger == 'click') {
17246                 on_el.on('click', this.toggle, this);
17247             } else if (trigger != 'manual') {
17248                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17249                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17250       
17251                 on_el.on(eventIn  ,this.enter, this);
17252                 on_el.on(eventOut, this.leave, this);
17253             }
17254         }, this);
17255         
17256     },
17257     
17258     
17259     // private
17260     timeout : null,
17261     hoverState : null,
17262     
17263     toggle : function () {
17264         this.hoverState == 'in' ? this.leave() : this.enter();
17265     },
17266     
17267     enter : function () {
17268         
17269         clearTimeout(this.timeout);
17270     
17271         this.hoverState = 'in';
17272     
17273         if (!this.delay || !this.delay.show) {
17274             this.show();
17275             return;
17276         }
17277         var _t = this;
17278         this.timeout = setTimeout(function () {
17279             if (_t.hoverState == 'in') {
17280                 _t.show();
17281             }
17282         }, this.delay.show)
17283     },
17284     
17285     leave : function() {
17286         clearTimeout(this.timeout);
17287     
17288         this.hoverState = 'out';
17289     
17290         if (!this.delay || !this.delay.hide) {
17291             this.hide();
17292             return;
17293         }
17294         var _t = this;
17295         this.timeout = setTimeout(function () {
17296             if (_t.hoverState == 'out') {
17297                 _t.hide();
17298             }
17299         }, this.delay.hide)
17300     },
17301     
17302     show : function (on_el)
17303     {
17304         if (!on_el) {
17305             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17306         }
17307         
17308         // set content.
17309         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17310         if (this.html !== false) {
17311             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17312         }
17313         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17314         if (!this.title.length) {
17315             this.el.select('.popover-title',true).hide();
17316         }
17317         
17318         var placement = typeof this.placement == 'function' ?
17319             this.placement.call(this, this.el, on_el) :
17320             this.placement;
17321             
17322         var autoToken = /\s?auto?\s?/i;
17323         var autoPlace = autoToken.test(placement);
17324         if (autoPlace) {
17325             placement = placement.replace(autoToken, '') || 'top';
17326         }
17327         
17328         //this.el.detach()
17329         //this.el.setXY([0,0]);
17330         this.el.show();
17331         this.el.dom.style.display='block';
17332         this.el.addClass(placement);
17333         
17334         //this.el.appendTo(on_el);
17335         
17336         var p = this.getPosition();
17337         var box = this.el.getBox();
17338         
17339         if (autoPlace) {
17340             // fixme..
17341         }
17342         var align = Roo.bootstrap.Popover.alignment[placement];
17343         
17344 //        Roo.log(align);
17345         this.el.alignTo(on_el, align[0],align[1]);
17346         //var arrow = this.el.select('.arrow',true).first();
17347         //arrow.set(align[2], 
17348         
17349         this.el.addClass('in');
17350         
17351         
17352         if (this.el.hasClass('fade')) {
17353             // fade it?
17354         }
17355         
17356         this.hoverState = 'in';
17357         
17358         this.fireEvent('show', this);
17359         
17360     },
17361     hide : function()
17362     {
17363         this.el.setXY([0,0]);
17364         this.el.removeClass('in');
17365         this.el.hide();
17366         this.hoverState = null;
17367         
17368         this.fireEvent('hide', this);
17369     }
17370     
17371 });
17372
17373 Roo.bootstrap.Popover.alignment = {
17374     'left' : ['r-l', [-10,0], 'right'],
17375     'right' : ['l-r', [10,0], 'left'],
17376     'bottom' : ['t-b', [0,10], 'top'],
17377     'top' : [ 'b-t', [0,-10], 'bottom']
17378 };
17379
17380  /*
17381  * - LGPL
17382  *
17383  * Progress
17384  * 
17385  */
17386
17387 /**
17388  * @class Roo.bootstrap.Progress
17389  * @extends Roo.bootstrap.Component
17390  * Bootstrap Progress class
17391  * @cfg {Boolean} striped striped of the progress bar
17392  * @cfg {Boolean} active animated of the progress bar
17393  * 
17394  * 
17395  * @constructor
17396  * Create a new Progress
17397  * @param {Object} config The config object
17398  */
17399
17400 Roo.bootstrap.Progress = function(config){
17401     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17402 };
17403
17404 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17405     
17406     striped : false,
17407     active: false,
17408     
17409     getAutoCreate : function(){
17410         var cfg = {
17411             tag: 'div',
17412             cls: 'progress'
17413         };
17414         
17415         
17416         if(this.striped){
17417             cfg.cls += ' progress-striped';
17418         }
17419       
17420         if(this.active){
17421             cfg.cls += ' active';
17422         }
17423         
17424         
17425         return cfg;
17426     }
17427    
17428 });
17429
17430  
17431
17432  /*
17433  * - LGPL
17434  *
17435  * ProgressBar
17436  * 
17437  */
17438
17439 /**
17440  * @class Roo.bootstrap.ProgressBar
17441  * @extends Roo.bootstrap.Component
17442  * Bootstrap ProgressBar class
17443  * @cfg {Number} aria_valuenow aria-value now
17444  * @cfg {Number} aria_valuemin aria-value min
17445  * @cfg {Number} aria_valuemax aria-value max
17446  * @cfg {String} label label for the progress bar
17447  * @cfg {String} panel (success | info | warning | danger )
17448  * @cfg {String} role role of the progress bar
17449  * @cfg {String} sr_only text
17450  * 
17451  * 
17452  * @constructor
17453  * Create a new ProgressBar
17454  * @param {Object} config The config object
17455  */
17456
17457 Roo.bootstrap.ProgressBar = function(config){
17458     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17459 };
17460
17461 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17462     
17463     aria_valuenow : 0,
17464     aria_valuemin : 0,
17465     aria_valuemax : 100,
17466     label : false,
17467     panel : false,
17468     role : false,
17469     sr_only: false,
17470     
17471     getAutoCreate : function()
17472     {
17473         
17474         var cfg = {
17475             tag: 'div',
17476             cls: 'progress-bar',
17477             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17478         };
17479         
17480         if(this.sr_only){
17481             cfg.cn = {
17482                 tag: 'span',
17483                 cls: 'sr-only',
17484                 html: this.sr_only
17485             }
17486         }
17487         
17488         if(this.role){
17489             cfg.role = this.role;
17490         }
17491         
17492         if(this.aria_valuenow){
17493             cfg['aria-valuenow'] = this.aria_valuenow;
17494         }
17495         
17496         if(this.aria_valuemin){
17497             cfg['aria-valuemin'] = this.aria_valuemin;
17498         }
17499         
17500         if(this.aria_valuemax){
17501             cfg['aria-valuemax'] = this.aria_valuemax;
17502         }
17503         
17504         if(this.label && !this.sr_only){
17505             cfg.html = this.label;
17506         }
17507         
17508         if(this.panel){
17509             cfg.cls += ' progress-bar-' + this.panel;
17510         }
17511         
17512         return cfg;
17513     },
17514     
17515     update : function(aria_valuenow)
17516     {
17517         this.aria_valuenow = aria_valuenow;
17518         
17519         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17520     }
17521    
17522 });
17523
17524  
17525
17526  /*
17527  * - LGPL
17528  *
17529  * column
17530  * 
17531  */
17532
17533 /**
17534  * @class Roo.bootstrap.TabGroup
17535  * @extends Roo.bootstrap.Column
17536  * Bootstrap Column class
17537  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17538  * @cfg {Boolean} carousel true to make the group behave like a carousel
17539  * @cfg {Boolean} bullets show bullets for the panels
17540  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17541  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17542  * @cfg {Boolean} showarrow (true|false) show arrow default true
17543  * 
17544  * @constructor
17545  * Create a new TabGroup
17546  * @param {Object} config The config object
17547  */
17548
17549 Roo.bootstrap.TabGroup = function(config){
17550     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17551     if (!this.navId) {
17552         this.navId = Roo.id();
17553     }
17554     this.tabs = [];
17555     Roo.bootstrap.TabGroup.register(this);
17556     
17557 };
17558
17559 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17560     
17561     carousel : false,
17562     transition : false,
17563     bullets : 0,
17564     timer : 0,
17565     autoslide : false,
17566     slideFn : false,
17567     slideOnTouch : false,
17568     showarrow : true,
17569     
17570     getAutoCreate : function()
17571     {
17572         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17573         
17574         cfg.cls += ' tab-content';
17575         
17576         if (this.carousel) {
17577             cfg.cls += ' carousel slide';
17578             
17579             cfg.cn = [{
17580                cls : 'carousel-inner',
17581                cn : []
17582             }];
17583         
17584             if(this.bullets  && !Roo.isTouch){
17585                 
17586                 var bullets = {
17587                     cls : 'carousel-bullets',
17588                     cn : []
17589                 };
17590                
17591                 if(this.bullets_cls){
17592                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17593                 }
17594                 
17595                 bullets.cn.push({
17596                     cls : 'clear'
17597                 });
17598                 
17599                 cfg.cn[0].cn.push(bullets);
17600             }
17601             
17602             if(this.showarrow){
17603                 cfg.cn[0].cn.push({
17604                     tag : 'div',
17605                     class : 'carousel-arrow',
17606                     cn : [
17607                         {
17608                             tag : 'div',
17609                             class : 'carousel-prev',
17610                             cn : [
17611                                 {
17612                                     tag : 'i',
17613                                     class : 'fa fa-chevron-left'
17614                                 }
17615                             ]
17616                         },
17617                         {
17618                             tag : 'div',
17619                             class : 'carousel-next',
17620                             cn : [
17621                                 {
17622                                     tag : 'i',
17623                                     class : 'fa fa-chevron-right'
17624                                 }
17625                             ]
17626                         }
17627                     ]
17628                 });
17629             }
17630             
17631         }
17632         
17633         return cfg;
17634     },
17635     
17636     initEvents:  function()
17637     {
17638 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17639 //            this.el.on("touchstart", this.onTouchStart, this);
17640 //        }
17641         
17642         if(this.autoslide){
17643             var _this = this;
17644             
17645             this.slideFn = window.setInterval(function() {
17646                 _this.showPanelNext();
17647             }, this.timer);
17648         }
17649         
17650         if(this.showarrow){
17651             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17652             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17653         }
17654         
17655         
17656     },
17657     
17658 //    onTouchStart : function(e, el, o)
17659 //    {
17660 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17661 //            return;
17662 //        }
17663 //        
17664 //        this.showPanelNext();
17665 //    },
17666     
17667     
17668     getChildContainer : function()
17669     {
17670         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17671     },
17672     
17673     /**
17674     * register a Navigation item
17675     * @param {Roo.bootstrap.NavItem} the navitem to add
17676     */
17677     register : function(item)
17678     {
17679         this.tabs.push( item);
17680         item.navId = this.navId; // not really needed..
17681         this.addBullet();
17682     
17683     },
17684     
17685     getActivePanel : function()
17686     {
17687         var r = false;
17688         Roo.each(this.tabs, function(t) {
17689             if (t.active) {
17690                 r = t;
17691                 return false;
17692             }
17693             return null;
17694         });
17695         return r;
17696         
17697     },
17698     getPanelByName : function(n)
17699     {
17700         var r = false;
17701         Roo.each(this.tabs, function(t) {
17702             if (t.tabId == n) {
17703                 r = t;
17704                 return false;
17705             }
17706             return null;
17707         });
17708         return r;
17709     },
17710     indexOfPanel : function(p)
17711     {
17712         var r = false;
17713         Roo.each(this.tabs, function(t,i) {
17714             if (t.tabId == p.tabId) {
17715                 r = i;
17716                 return false;
17717             }
17718             return null;
17719         });
17720         return r;
17721     },
17722     /**
17723      * show a specific panel
17724      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17725      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17726      */
17727     showPanel : function (pan)
17728     {
17729         if(this.transition || typeof(pan) == 'undefined'){
17730             Roo.log("waiting for the transitionend");
17731             return;
17732         }
17733         
17734         if (typeof(pan) == 'number') {
17735             pan = this.tabs[pan];
17736         }
17737         
17738         if (typeof(pan) == 'string') {
17739             pan = this.getPanelByName(pan);
17740         }
17741         
17742         var cur = this.getActivePanel();
17743         
17744         if(!pan || !cur){
17745             Roo.log('pan or acitve pan is undefined');
17746             return false;
17747         }
17748         
17749         if (pan.tabId == this.getActivePanel().tabId) {
17750             return true;
17751         }
17752         
17753         if (false === cur.fireEvent('beforedeactivate')) {
17754             return false;
17755         }
17756         
17757         if(this.bullets > 0 && !Roo.isTouch){
17758             this.setActiveBullet(this.indexOfPanel(pan));
17759         }
17760         
17761         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17762             
17763             this.transition = true;
17764             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17765             var lr = dir == 'next' ? 'left' : 'right';
17766             pan.el.addClass(dir); // or prev
17767             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17768             cur.el.addClass(lr); // or right
17769             pan.el.addClass(lr);
17770             
17771             var _this = this;
17772             cur.el.on('transitionend', function() {
17773                 Roo.log("trans end?");
17774                 
17775                 pan.el.removeClass([lr,dir]);
17776                 pan.setActive(true);
17777                 
17778                 cur.el.removeClass([lr]);
17779                 cur.setActive(false);
17780                 
17781                 _this.transition = false;
17782                 
17783             }, this, { single:  true } );
17784             
17785             return true;
17786         }
17787         
17788         cur.setActive(false);
17789         pan.setActive(true);
17790         
17791         return true;
17792         
17793     },
17794     showPanelNext : function()
17795     {
17796         var i = this.indexOfPanel(this.getActivePanel());
17797         
17798         if (i >= this.tabs.length - 1 && !this.autoslide) {
17799             return;
17800         }
17801         
17802         if (i >= this.tabs.length - 1 && this.autoslide) {
17803             i = -1;
17804         }
17805         
17806         this.showPanel(this.tabs[i+1]);
17807     },
17808     
17809     showPanelPrev : function()
17810     {
17811         var i = this.indexOfPanel(this.getActivePanel());
17812         
17813         if (i  < 1 && !this.autoslide) {
17814             return;
17815         }
17816         
17817         if (i < 1 && this.autoslide) {
17818             i = this.tabs.length;
17819         }
17820         
17821         this.showPanel(this.tabs[i-1]);
17822     },
17823     
17824     
17825     addBullet: function()
17826     {
17827         if(!this.bullets || Roo.isTouch){
17828             return;
17829         }
17830         var ctr = this.el.select('.carousel-bullets',true).first();
17831         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17832         var bullet = ctr.createChild({
17833             cls : 'bullet bullet-' + i
17834         },ctr.dom.lastChild);
17835         
17836         
17837         var _this = this;
17838         
17839         bullet.on('click', (function(e, el, o, ii, t){
17840
17841             e.preventDefault();
17842
17843             this.showPanel(ii);
17844
17845             if(this.autoslide && this.slideFn){
17846                 clearInterval(this.slideFn);
17847                 this.slideFn = window.setInterval(function() {
17848                     _this.showPanelNext();
17849                 }, this.timer);
17850             }
17851
17852         }).createDelegate(this, [i, bullet], true));
17853                 
17854         
17855     },
17856      
17857     setActiveBullet : function(i)
17858     {
17859         if(Roo.isTouch){
17860             return;
17861         }
17862         
17863         Roo.each(this.el.select('.bullet', true).elements, function(el){
17864             el.removeClass('selected');
17865         });
17866
17867         var bullet = this.el.select('.bullet-' + i, true).first();
17868         
17869         if(!bullet){
17870             return;
17871         }
17872         
17873         bullet.addClass('selected');
17874     }
17875     
17876     
17877   
17878 });
17879
17880  
17881
17882  
17883  
17884 Roo.apply(Roo.bootstrap.TabGroup, {
17885     
17886     groups: {},
17887      /**
17888     * register a Navigation Group
17889     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17890     */
17891     register : function(navgrp)
17892     {
17893         this.groups[navgrp.navId] = navgrp;
17894         
17895     },
17896     /**
17897     * fetch a Navigation Group based on the navigation ID
17898     * if one does not exist , it will get created.
17899     * @param {string} the navgroup to add
17900     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17901     */
17902     get: function(navId) {
17903         if (typeof(this.groups[navId]) == 'undefined') {
17904             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17905         }
17906         return this.groups[navId] ;
17907     }
17908     
17909     
17910     
17911 });
17912
17913  /*
17914  * - LGPL
17915  *
17916  * TabPanel
17917  * 
17918  */
17919
17920 /**
17921  * @class Roo.bootstrap.TabPanel
17922  * @extends Roo.bootstrap.Component
17923  * Bootstrap TabPanel class
17924  * @cfg {Boolean} active panel active
17925  * @cfg {String} html panel content
17926  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17927  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17928  * @cfg {String} href click to link..
17929  * 
17930  * 
17931  * @constructor
17932  * Create a new TabPanel
17933  * @param {Object} config The config object
17934  */
17935
17936 Roo.bootstrap.TabPanel = function(config){
17937     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17938     this.addEvents({
17939         /**
17940              * @event changed
17941              * Fires when the active status changes
17942              * @param {Roo.bootstrap.TabPanel} this
17943              * @param {Boolean} state the new state
17944             
17945          */
17946         'changed': true,
17947         /**
17948              * @event beforedeactivate
17949              * Fires before a tab is de-activated - can be used to do validation on a form.
17950              * @param {Roo.bootstrap.TabPanel} this
17951              * @return {Boolean} false if there is an error
17952             
17953          */
17954         'beforedeactivate': true
17955      });
17956     
17957     this.tabId = this.tabId || Roo.id();
17958   
17959 };
17960
17961 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17962     
17963     active: false,
17964     html: false,
17965     tabId: false,
17966     navId : false,
17967     href : '',
17968     
17969     getAutoCreate : function(){
17970         var cfg = {
17971             tag: 'div',
17972             // item is needed for carousel - not sure if it has any effect otherwise
17973             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17974             html: this.html || ''
17975         };
17976         
17977         if(this.active){
17978             cfg.cls += ' active';
17979         }
17980         
17981         if(this.tabId){
17982             cfg.tabId = this.tabId;
17983         }
17984         
17985         
17986         return cfg;
17987     },
17988     
17989     initEvents:  function()
17990     {
17991         var p = this.parent();
17992         
17993         this.navId = this.navId || p.navId;
17994         
17995         if (typeof(this.navId) != 'undefined') {
17996             // not really needed.. but just in case.. parent should be a NavGroup.
17997             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17998             
17999             tg.register(this);
18000             
18001             var i = tg.tabs.length - 1;
18002             
18003             if(this.active && tg.bullets > 0 && i < tg.bullets){
18004                 tg.setActiveBullet(i);
18005             }
18006         }
18007         
18008         this.el.on('click', this.onClick, this);
18009         
18010         if(Roo.isTouch){
18011             this.el.on("touchstart", this.onTouchStart, this);
18012             this.el.on("touchmove", this.onTouchMove, this);
18013             this.el.on("touchend", this.onTouchEnd, this);
18014         }
18015         
18016     },
18017     
18018     onRender : function(ct, position)
18019     {
18020         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18021     },
18022     
18023     setActive : function(state)
18024     {
18025         Roo.log("panel - set active " + this.tabId + "=" + state);
18026         
18027         this.active = state;
18028         if (!state) {
18029             this.el.removeClass('active');
18030             
18031         } else  if (!this.el.hasClass('active')) {
18032             this.el.addClass('active');
18033         }
18034         
18035         this.fireEvent('changed', this, state);
18036     },
18037     
18038     onClick : function(e)
18039     {
18040         e.preventDefault();
18041         
18042         if(!this.href.length){
18043             return;
18044         }
18045         
18046         window.location.href = this.href;
18047     },
18048     
18049     startX : 0,
18050     startY : 0,
18051     endX : 0,
18052     endY : 0,
18053     swiping : false,
18054     
18055     onTouchStart : function(e)
18056     {
18057         this.swiping = false;
18058         
18059         this.startX = e.browserEvent.touches[0].clientX;
18060         this.startY = e.browserEvent.touches[0].clientY;
18061     },
18062     
18063     onTouchMove : function(e)
18064     {
18065         this.swiping = true;
18066         
18067         this.endX = e.browserEvent.touches[0].clientX;
18068         this.endY = e.browserEvent.touches[0].clientY;
18069     },
18070     
18071     onTouchEnd : function(e)
18072     {
18073         if(!this.swiping){
18074             this.onClick(e);
18075             return;
18076         }
18077         
18078         var tabGroup = this.parent();
18079         
18080         if(this.endX > this.startX){ // swiping right
18081             tabGroup.showPanelPrev();
18082             return;
18083         }
18084         
18085         if(this.startX > this.endX){ // swiping left
18086             tabGroup.showPanelNext();
18087             return;
18088         }
18089     }
18090     
18091     
18092 });
18093  
18094
18095  
18096
18097  /*
18098  * - LGPL
18099  *
18100  * DateField
18101  * 
18102  */
18103
18104 /**
18105  * @class Roo.bootstrap.DateField
18106  * @extends Roo.bootstrap.Input
18107  * Bootstrap DateField class
18108  * @cfg {Number} weekStart default 0
18109  * @cfg {String} viewMode default empty, (months|years)
18110  * @cfg {String} minViewMode default empty, (months|years)
18111  * @cfg {Number} startDate default -Infinity
18112  * @cfg {Number} endDate default Infinity
18113  * @cfg {Boolean} todayHighlight default false
18114  * @cfg {Boolean} todayBtn default false
18115  * @cfg {Boolean} calendarWeeks default false
18116  * @cfg {Object} daysOfWeekDisabled default empty
18117  * @cfg {Boolean} singleMode default false (true | false)
18118  * 
18119  * @cfg {Boolean} keyboardNavigation default true
18120  * @cfg {String} language default en
18121  * 
18122  * @constructor
18123  * Create a new DateField
18124  * @param {Object} config The config object
18125  */
18126
18127 Roo.bootstrap.DateField = function(config){
18128     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18129      this.addEvents({
18130             /**
18131              * @event show
18132              * Fires when this field show.
18133              * @param {Roo.bootstrap.DateField} this
18134              * @param {Mixed} date The date value
18135              */
18136             show : true,
18137             /**
18138              * @event show
18139              * Fires when this field hide.
18140              * @param {Roo.bootstrap.DateField} this
18141              * @param {Mixed} date The date value
18142              */
18143             hide : true,
18144             /**
18145              * @event select
18146              * Fires when select a date.
18147              * @param {Roo.bootstrap.DateField} this
18148              * @param {Mixed} date The date value
18149              */
18150             select : true,
18151             /**
18152              * @event beforeselect
18153              * Fires when before select a date.
18154              * @param {Roo.bootstrap.DateField} this
18155              * @param {Mixed} date The date value
18156              */
18157             beforeselect : true
18158         });
18159 };
18160
18161 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18162     
18163     /**
18164      * @cfg {String} format
18165      * The default date format string which can be overriden for localization support.  The format must be
18166      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18167      */
18168     format : "m/d/y",
18169     /**
18170      * @cfg {String} altFormats
18171      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18172      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18173      */
18174     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18175     
18176     weekStart : 0,
18177     
18178     viewMode : '',
18179     
18180     minViewMode : '',
18181     
18182     todayHighlight : false,
18183     
18184     todayBtn: false,
18185     
18186     language: 'en',
18187     
18188     keyboardNavigation: true,
18189     
18190     calendarWeeks: false,
18191     
18192     startDate: -Infinity,
18193     
18194     endDate: Infinity,
18195     
18196     daysOfWeekDisabled: [],
18197     
18198     _events: [],
18199     
18200     singleMode : false,
18201     
18202     UTCDate: function()
18203     {
18204         return new Date(Date.UTC.apply(Date, arguments));
18205     },
18206     
18207     UTCToday: function()
18208     {
18209         var today = new Date();
18210         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18211     },
18212     
18213     getDate: function() {
18214             var d = this.getUTCDate();
18215             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18216     },
18217     
18218     getUTCDate: function() {
18219             return this.date;
18220     },
18221     
18222     setDate: function(d) {
18223             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18224     },
18225     
18226     setUTCDate: function(d) {
18227             this.date = d;
18228             this.setValue(this.formatDate(this.date));
18229     },
18230         
18231     onRender: function(ct, position)
18232     {
18233         
18234         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18235         
18236         this.language = this.language || 'en';
18237         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18238         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18239         
18240         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18241         this.format = this.format || 'm/d/y';
18242         this.isInline = false;
18243         this.isInput = true;
18244         this.component = this.el.select('.add-on', true).first() || false;
18245         this.component = (this.component && this.component.length === 0) ? false : this.component;
18246         this.hasInput = this.component && this.inputEl().length;
18247         
18248         if (typeof(this.minViewMode === 'string')) {
18249             switch (this.minViewMode) {
18250                 case 'months':
18251                     this.minViewMode = 1;
18252                     break;
18253                 case 'years':
18254                     this.minViewMode = 2;
18255                     break;
18256                 default:
18257                     this.minViewMode = 0;
18258                     break;
18259             }
18260         }
18261         
18262         if (typeof(this.viewMode === 'string')) {
18263             switch (this.viewMode) {
18264                 case 'months':
18265                     this.viewMode = 1;
18266                     break;
18267                 case 'years':
18268                     this.viewMode = 2;
18269                     break;
18270                 default:
18271                     this.viewMode = 0;
18272                     break;
18273             }
18274         }
18275                 
18276         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18277         
18278 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18279         
18280         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18281         
18282         this.picker().on('mousedown', this.onMousedown, this);
18283         this.picker().on('click', this.onClick, this);
18284         
18285         this.picker().addClass('datepicker-dropdown');
18286         
18287         this.startViewMode = this.viewMode;
18288         
18289         if(this.singleMode){
18290             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18291                 v.setVisibilityMode(Roo.Element.DISPLAY);
18292                 v.hide();
18293             });
18294             
18295             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18296                 v.setStyle('width', '189px');
18297             });
18298         }
18299         
18300         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18301             if(!this.calendarWeeks){
18302                 v.remove();
18303                 return;
18304             }
18305             
18306             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18307             v.attr('colspan', function(i, val){
18308                 return parseInt(val) + 1;
18309             });
18310         });
18311                         
18312         
18313         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18314         
18315         this.setStartDate(this.startDate);
18316         this.setEndDate(this.endDate);
18317         
18318         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18319         
18320         this.fillDow();
18321         this.fillMonths();
18322         this.update();
18323         this.showMode();
18324         
18325         if(this.isInline) {
18326             this.show();
18327         }
18328     },
18329     
18330     picker : function()
18331     {
18332         return this.pickerEl;
18333 //        return this.el.select('.datepicker', true).first();
18334     },
18335     
18336     fillDow: function()
18337     {
18338         var dowCnt = this.weekStart;
18339         
18340         var dow = {
18341             tag: 'tr',
18342             cn: [
18343                 
18344             ]
18345         };
18346         
18347         if(this.calendarWeeks){
18348             dow.cn.push({
18349                 tag: 'th',
18350                 cls: 'cw',
18351                 html: '&nbsp;'
18352             })
18353         }
18354         
18355         while (dowCnt < this.weekStart + 7) {
18356             dow.cn.push({
18357                 tag: 'th',
18358                 cls: 'dow',
18359                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18360             });
18361         }
18362         
18363         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18364     },
18365     
18366     fillMonths: function()
18367     {    
18368         var i = 0;
18369         var months = this.picker().select('>.datepicker-months td', true).first();
18370         
18371         months.dom.innerHTML = '';
18372         
18373         while (i < 12) {
18374             var month = {
18375                 tag: 'span',
18376                 cls: 'month',
18377                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18378             };
18379             
18380             months.createChild(month);
18381         }
18382         
18383     },
18384     
18385     update: function()
18386     {
18387         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;
18388         
18389         if (this.date < this.startDate) {
18390             this.viewDate = new Date(this.startDate);
18391         } else if (this.date > this.endDate) {
18392             this.viewDate = new Date(this.endDate);
18393         } else {
18394             this.viewDate = new Date(this.date);
18395         }
18396         
18397         this.fill();
18398     },
18399     
18400     fill: function() 
18401     {
18402         var d = new Date(this.viewDate),
18403                 year = d.getUTCFullYear(),
18404                 month = d.getUTCMonth(),
18405                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18406                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18407                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18408                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18409                 currentDate = this.date && this.date.valueOf(),
18410                 today = this.UTCToday();
18411         
18412         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18413         
18414 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18415         
18416 //        this.picker.select('>tfoot th.today').
18417 //                                              .text(dates[this.language].today)
18418 //                                              .toggle(this.todayBtn !== false);
18419     
18420         this.updateNavArrows();
18421         this.fillMonths();
18422                                                 
18423         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18424         
18425         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18426          
18427         prevMonth.setUTCDate(day);
18428         
18429         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18430         
18431         var nextMonth = new Date(prevMonth);
18432         
18433         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18434         
18435         nextMonth = nextMonth.valueOf();
18436         
18437         var fillMonths = false;
18438         
18439         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18440         
18441         while(prevMonth.valueOf() < nextMonth) {
18442             var clsName = '';
18443             
18444             if (prevMonth.getUTCDay() === this.weekStart) {
18445                 if(fillMonths){
18446                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18447                 }
18448                     
18449                 fillMonths = {
18450                     tag: 'tr',
18451                     cn: []
18452                 };
18453                 
18454                 if(this.calendarWeeks){
18455                     // ISO 8601: First week contains first thursday.
18456                     // ISO also states week starts on Monday, but we can be more abstract here.
18457                     var
18458                     // Start of current week: based on weekstart/current date
18459                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18460                     // Thursday of this week
18461                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18462                     // First Thursday of year, year from thursday
18463                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18464                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18465                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18466                     
18467                     fillMonths.cn.push({
18468                         tag: 'td',
18469                         cls: 'cw',
18470                         html: calWeek
18471                     });
18472                 }
18473             }
18474             
18475             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18476                 clsName += ' old';
18477             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18478                 clsName += ' new';
18479             }
18480             if (this.todayHighlight &&
18481                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18482                 prevMonth.getUTCMonth() == today.getMonth() &&
18483                 prevMonth.getUTCDate() == today.getDate()) {
18484                 clsName += ' today';
18485             }
18486             
18487             if (currentDate && prevMonth.valueOf() === currentDate) {
18488                 clsName += ' active';
18489             }
18490             
18491             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18492                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18493                     clsName += ' disabled';
18494             }
18495             
18496             fillMonths.cn.push({
18497                 tag: 'td',
18498                 cls: 'day ' + clsName,
18499                 html: prevMonth.getDate()
18500             });
18501             
18502             prevMonth.setDate(prevMonth.getDate()+1);
18503         }
18504           
18505         var currentYear = this.date && this.date.getUTCFullYear();
18506         var currentMonth = this.date && this.date.getUTCMonth();
18507         
18508         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18509         
18510         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18511             v.removeClass('active');
18512             
18513             if(currentYear === year && k === currentMonth){
18514                 v.addClass('active');
18515             }
18516             
18517             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18518                 v.addClass('disabled');
18519             }
18520             
18521         });
18522         
18523         
18524         year = parseInt(year/10, 10) * 10;
18525         
18526         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18527         
18528         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18529         
18530         year -= 1;
18531         for (var i = -1; i < 11; i++) {
18532             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18533                 tag: 'span',
18534                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18535                 html: year
18536             });
18537             
18538             year += 1;
18539         }
18540     },
18541     
18542     showMode: function(dir) 
18543     {
18544         if (dir) {
18545             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18546         }
18547         
18548         Roo.each(this.picker().select('>div',true).elements, function(v){
18549             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18550             v.hide();
18551         });
18552         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18553     },
18554     
18555     place: function()
18556     {
18557         if(this.isInline) {
18558             return;
18559         }
18560         
18561         this.picker().removeClass(['bottom', 'top']);
18562         
18563         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18564             /*
18565              * place to the top of element!
18566              *
18567              */
18568             
18569             this.picker().addClass('top');
18570             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18571             
18572             return;
18573         }
18574         
18575         this.picker().addClass('bottom');
18576         
18577         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18578     },
18579     
18580     parseDate : function(value)
18581     {
18582         if(!value || value instanceof Date){
18583             return value;
18584         }
18585         var v = Date.parseDate(value, this.format);
18586         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18587             v = Date.parseDate(value, 'Y-m-d');
18588         }
18589         if(!v && this.altFormats){
18590             if(!this.altFormatsArray){
18591                 this.altFormatsArray = this.altFormats.split("|");
18592             }
18593             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18594                 v = Date.parseDate(value, this.altFormatsArray[i]);
18595             }
18596         }
18597         return v;
18598     },
18599     
18600     formatDate : function(date, fmt)
18601     {   
18602         return (!date || !(date instanceof Date)) ?
18603         date : date.dateFormat(fmt || this.format);
18604     },
18605     
18606     onFocus : function()
18607     {
18608         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18609         this.show();
18610     },
18611     
18612     onBlur : function()
18613     {
18614         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18615         
18616         var d = this.inputEl().getValue();
18617         
18618         this.setValue(d);
18619                 
18620         this.hide();
18621     },
18622     
18623     show : function()
18624     {
18625         this.picker().show();
18626         this.update();
18627         this.place();
18628         
18629         this.fireEvent('show', this, this.date);
18630     },
18631     
18632     hide : function()
18633     {
18634         if(this.isInline) {
18635             return;
18636         }
18637         this.picker().hide();
18638         this.viewMode = this.startViewMode;
18639         this.showMode();
18640         
18641         this.fireEvent('hide', this, this.date);
18642         
18643     },
18644     
18645     onMousedown: function(e)
18646     {
18647         e.stopPropagation();
18648         e.preventDefault();
18649     },
18650     
18651     keyup: function(e)
18652     {
18653         Roo.bootstrap.DateField.superclass.keyup.call(this);
18654         this.update();
18655     },
18656
18657     setValue: function(v)
18658     {
18659         if(this.fireEvent('beforeselect', this, v) !== false){
18660             var d = new Date(this.parseDate(v) ).clearTime();
18661         
18662             if(isNaN(d.getTime())){
18663                 this.date = this.viewDate = '';
18664                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18665                 return;
18666             }
18667
18668             v = this.formatDate(d);
18669
18670             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18671
18672             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18673
18674             this.update();
18675
18676             this.fireEvent('select', this, this.date);
18677         }
18678     },
18679     
18680     getValue: function()
18681     {
18682         return this.formatDate(this.date);
18683     },
18684     
18685     fireKey: function(e)
18686     {
18687         if (!this.picker().isVisible()){
18688             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18689                 this.show();
18690             }
18691             return;
18692         }
18693         
18694         var dateChanged = false,
18695         dir, day, month,
18696         newDate, newViewDate;
18697         
18698         switch(e.keyCode){
18699             case 27: // escape
18700                 this.hide();
18701                 e.preventDefault();
18702                 break;
18703             case 37: // left
18704             case 39: // right
18705                 if (!this.keyboardNavigation) {
18706                     break;
18707                 }
18708                 dir = e.keyCode == 37 ? -1 : 1;
18709                 
18710                 if (e.ctrlKey){
18711                     newDate = this.moveYear(this.date, dir);
18712                     newViewDate = this.moveYear(this.viewDate, dir);
18713                 } else if (e.shiftKey){
18714                     newDate = this.moveMonth(this.date, dir);
18715                     newViewDate = this.moveMonth(this.viewDate, dir);
18716                 } else {
18717                     newDate = new Date(this.date);
18718                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18719                     newViewDate = new Date(this.viewDate);
18720                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18721                 }
18722                 if (this.dateWithinRange(newDate)){
18723                     this.date = newDate;
18724                     this.viewDate = newViewDate;
18725                     this.setValue(this.formatDate(this.date));
18726 //                    this.update();
18727                     e.preventDefault();
18728                     dateChanged = true;
18729                 }
18730                 break;
18731             case 38: // up
18732             case 40: // down
18733                 if (!this.keyboardNavigation) {
18734                     break;
18735                 }
18736                 dir = e.keyCode == 38 ? -1 : 1;
18737                 if (e.ctrlKey){
18738                     newDate = this.moveYear(this.date, dir);
18739                     newViewDate = this.moveYear(this.viewDate, dir);
18740                 } else if (e.shiftKey){
18741                     newDate = this.moveMonth(this.date, dir);
18742                     newViewDate = this.moveMonth(this.viewDate, dir);
18743                 } else {
18744                     newDate = new Date(this.date);
18745                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18746                     newViewDate = new Date(this.viewDate);
18747                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18748                 }
18749                 if (this.dateWithinRange(newDate)){
18750                     this.date = newDate;
18751                     this.viewDate = newViewDate;
18752                     this.setValue(this.formatDate(this.date));
18753 //                    this.update();
18754                     e.preventDefault();
18755                     dateChanged = true;
18756                 }
18757                 break;
18758             case 13: // enter
18759                 this.setValue(this.formatDate(this.date));
18760                 this.hide();
18761                 e.preventDefault();
18762                 break;
18763             case 9: // tab
18764                 this.setValue(this.formatDate(this.date));
18765                 this.hide();
18766                 break;
18767             case 16: // shift
18768             case 17: // ctrl
18769             case 18: // alt
18770                 break;
18771             default :
18772                 this.hide();
18773                 
18774         }
18775     },
18776     
18777     
18778     onClick: function(e) 
18779     {
18780         e.stopPropagation();
18781         e.preventDefault();
18782         
18783         var target = e.getTarget();
18784         
18785         if(target.nodeName.toLowerCase() === 'i'){
18786             target = Roo.get(target).dom.parentNode;
18787         }
18788         
18789         var nodeName = target.nodeName;
18790         var className = target.className;
18791         var html = target.innerHTML;
18792         //Roo.log(nodeName);
18793         
18794         switch(nodeName.toLowerCase()) {
18795             case 'th':
18796                 switch(className) {
18797                     case 'switch':
18798                         this.showMode(1);
18799                         break;
18800                     case 'prev':
18801                     case 'next':
18802                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18803                         switch(this.viewMode){
18804                                 case 0:
18805                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18806                                         break;
18807                                 case 1:
18808                                 case 2:
18809                                         this.viewDate = this.moveYear(this.viewDate, dir);
18810                                         break;
18811                         }
18812                         this.fill();
18813                         break;
18814                     case 'today':
18815                         var date = new Date();
18816                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18817 //                        this.fill()
18818                         this.setValue(this.formatDate(this.date));
18819                         
18820                         this.hide();
18821                         break;
18822                 }
18823                 break;
18824             case 'span':
18825                 if (className.indexOf('disabled') < 0) {
18826                     this.viewDate.setUTCDate(1);
18827                     if (className.indexOf('month') > -1) {
18828                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18829                     } else {
18830                         var year = parseInt(html, 10) || 0;
18831                         this.viewDate.setUTCFullYear(year);
18832                         
18833                     }
18834                     
18835                     if(this.singleMode){
18836                         this.setValue(this.formatDate(this.viewDate));
18837                         this.hide();
18838                         return;
18839                     }
18840                     
18841                     this.showMode(-1);
18842                     this.fill();
18843                 }
18844                 break;
18845                 
18846             case 'td':
18847                 //Roo.log(className);
18848                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18849                     var day = parseInt(html, 10) || 1;
18850                     var year = this.viewDate.getUTCFullYear(),
18851                         month = this.viewDate.getUTCMonth();
18852
18853                     if (className.indexOf('old') > -1) {
18854                         if(month === 0 ){
18855                             month = 11;
18856                             year -= 1;
18857                         }else{
18858                             month -= 1;
18859                         }
18860                     } else if (className.indexOf('new') > -1) {
18861                         if (month == 11) {
18862                             month = 0;
18863                             year += 1;
18864                         } else {
18865                             month += 1;
18866                         }
18867                     }
18868                     //Roo.log([year,month,day]);
18869                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18870                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18871 //                    this.fill();
18872                     //Roo.log(this.formatDate(this.date));
18873                     this.setValue(this.formatDate(this.date));
18874                     this.hide();
18875                 }
18876                 break;
18877         }
18878     },
18879     
18880     setStartDate: function(startDate)
18881     {
18882         this.startDate = startDate || -Infinity;
18883         if (this.startDate !== -Infinity) {
18884             this.startDate = this.parseDate(this.startDate);
18885         }
18886         this.update();
18887         this.updateNavArrows();
18888     },
18889
18890     setEndDate: function(endDate)
18891     {
18892         this.endDate = endDate || Infinity;
18893         if (this.endDate !== Infinity) {
18894             this.endDate = this.parseDate(this.endDate);
18895         }
18896         this.update();
18897         this.updateNavArrows();
18898     },
18899     
18900     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18901     {
18902         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18903         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18904             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18905         }
18906         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18907             return parseInt(d, 10);
18908         });
18909         this.update();
18910         this.updateNavArrows();
18911     },
18912     
18913     updateNavArrows: function() 
18914     {
18915         if(this.singleMode){
18916             return;
18917         }
18918         
18919         var d = new Date(this.viewDate),
18920         year = d.getUTCFullYear(),
18921         month = d.getUTCMonth();
18922         
18923         Roo.each(this.picker().select('.prev', true).elements, function(v){
18924             v.show();
18925             switch (this.viewMode) {
18926                 case 0:
18927
18928                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18929                         v.hide();
18930                     }
18931                     break;
18932                 case 1:
18933                 case 2:
18934                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18935                         v.hide();
18936                     }
18937                     break;
18938             }
18939         });
18940         
18941         Roo.each(this.picker().select('.next', true).elements, function(v){
18942             v.show();
18943             switch (this.viewMode) {
18944                 case 0:
18945
18946                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18947                         v.hide();
18948                     }
18949                     break;
18950                 case 1:
18951                 case 2:
18952                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18953                         v.hide();
18954                     }
18955                     break;
18956             }
18957         })
18958     },
18959     
18960     moveMonth: function(date, dir)
18961     {
18962         if (!dir) {
18963             return date;
18964         }
18965         var new_date = new Date(date.valueOf()),
18966         day = new_date.getUTCDate(),
18967         month = new_date.getUTCMonth(),
18968         mag = Math.abs(dir),
18969         new_month, test;
18970         dir = dir > 0 ? 1 : -1;
18971         if (mag == 1){
18972             test = dir == -1
18973             // If going back one month, make sure month is not current month
18974             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18975             ? function(){
18976                 return new_date.getUTCMonth() == month;
18977             }
18978             // If going forward one month, make sure month is as expected
18979             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18980             : function(){
18981                 return new_date.getUTCMonth() != new_month;
18982             };
18983             new_month = month + dir;
18984             new_date.setUTCMonth(new_month);
18985             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18986             if (new_month < 0 || new_month > 11) {
18987                 new_month = (new_month + 12) % 12;
18988             }
18989         } else {
18990             // For magnitudes >1, move one month at a time...
18991             for (var i=0; i<mag; i++) {
18992                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18993                 new_date = this.moveMonth(new_date, dir);
18994             }
18995             // ...then reset the day, keeping it in the new month
18996             new_month = new_date.getUTCMonth();
18997             new_date.setUTCDate(day);
18998             test = function(){
18999                 return new_month != new_date.getUTCMonth();
19000             };
19001         }
19002         // Common date-resetting loop -- if date is beyond end of month, make it
19003         // end of month
19004         while (test()){
19005             new_date.setUTCDate(--day);
19006             new_date.setUTCMonth(new_month);
19007         }
19008         return new_date;
19009     },
19010
19011     moveYear: function(date, dir)
19012     {
19013         return this.moveMonth(date, dir*12);
19014     },
19015
19016     dateWithinRange: function(date)
19017     {
19018         return date >= this.startDate && date <= this.endDate;
19019     },
19020
19021     
19022     remove: function() 
19023     {
19024         this.picker().remove();
19025     },
19026     
19027     validateValue : function(value)
19028     {
19029         if(value.length < 1)  {
19030             if(this.allowBlank){
19031                 return true;
19032             }
19033             return false;
19034         }
19035         
19036         if(value.length < this.minLength){
19037             return false;
19038         }
19039         if(value.length > this.maxLength){
19040             return false;
19041         }
19042         if(this.vtype){
19043             var vt = Roo.form.VTypes;
19044             if(!vt[this.vtype](value, this)){
19045                 return false;
19046             }
19047         }
19048         if(typeof this.validator == "function"){
19049             var msg = this.validator(value);
19050             if(msg !== true){
19051                 return false;
19052             }
19053         }
19054         
19055         if(this.regex && !this.regex.test(value)){
19056             return false;
19057         }
19058         
19059         if(typeof(this.parseDate(value)) == 'undefined'){
19060             return false;
19061         }
19062         
19063         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19064             return false;
19065         }      
19066         
19067         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19068             return false;
19069         } 
19070         
19071         
19072         return true;
19073     }
19074    
19075 });
19076
19077 Roo.apply(Roo.bootstrap.DateField,  {
19078     
19079     head : {
19080         tag: 'thead',
19081         cn: [
19082         {
19083             tag: 'tr',
19084             cn: [
19085             {
19086                 tag: 'th',
19087                 cls: 'prev',
19088                 html: '<i class="fa fa-arrow-left"/>'
19089             },
19090             {
19091                 tag: 'th',
19092                 cls: 'switch',
19093                 colspan: '5'
19094             },
19095             {
19096                 tag: 'th',
19097                 cls: 'next',
19098                 html: '<i class="fa fa-arrow-right"/>'
19099             }
19100
19101             ]
19102         }
19103         ]
19104     },
19105     
19106     content : {
19107         tag: 'tbody',
19108         cn: [
19109         {
19110             tag: 'tr',
19111             cn: [
19112             {
19113                 tag: 'td',
19114                 colspan: '7'
19115             }
19116             ]
19117         }
19118         ]
19119     },
19120     
19121     footer : {
19122         tag: 'tfoot',
19123         cn: [
19124         {
19125             tag: 'tr',
19126             cn: [
19127             {
19128                 tag: 'th',
19129                 colspan: '7',
19130                 cls: 'today'
19131             }
19132                     
19133             ]
19134         }
19135         ]
19136     },
19137     
19138     dates:{
19139         en: {
19140             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19141             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19142             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19143             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19144             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19145             today: "Today"
19146         }
19147     },
19148     
19149     modes: [
19150     {
19151         clsName: 'days',
19152         navFnc: 'Month',
19153         navStep: 1
19154     },
19155     {
19156         clsName: 'months',
19157         navFnc: 'FullYear',
19158         navStep: 1
19159     },
19160     {
19161         clsName: 'years',
19162         navFnc: 'FullYear',
19163         navStep: 10
19164     }]
19165 });
19166
19167 Roo.apply(Roo.bootstrap.DateField,  {
19168   
19169     template : {
19170         tag: 'div',
19171         cls: 'datepicker dropdown-menu roo-dynamic',
19172         cn: [
19173         {
19174             tag: 'div',
19175             cls: 'datepicker-days',
19176             cn: [
19177             {
19178                 tag: 'table',
19179                 cls: 'table-condensed',
19180                 cn:[
19181                 Roo.bootstrap.DateField.head,
19182                 {
19183                     tag: 'tbody'
19184                 },
19185                 Roo.bootstrap.DateField.footer
19186                 ]
19187             }
19188             ]
19189         },
19190         {
19191             tag: 'div',
19192             cls: 'datepicker-months',
19193             cn: [
19194             {
19195                 tag: 'table',
19196                 cls: 'table-condensed',
19197                 cn:[
19198                 Roo.bootstrap.DateField.head,
19199                 Roo.bootstrap.DateField.content,
19200                 Roo.bootstrap.DateField.footer
19201                 ]
19202             }
19203             ]
19204         },
19205         {
19206             tag: 'div',
19207             cls: 'datepicker-years',
19208             cn: [
19209             {
19210                 tag: 'table',
19211                 cls: 'table-condensed',
19212                 cn:[
19213                 Roo.bootstrap.DateField.head,
19214                 Roo.bootstrap.DateField.content,
19215                 Roo.bootstrap.DateField.footer
19216                 ]
19217             }
19218             ]
19219         }
19220         ]
19221     }
19222 });
19223
19224  
19225
19226  /*
19227  * - LGPL
19228  *
19229  * TimeField
19230  * 
19231  */
19232
19233 /**
19234  * @class Roo.bootstrap.TimeField
19235  * @extends Roo.bootstrap.Input
19236  * Bootstrap DateField class
19237  * 
19238  * 
19239  * @constructor
19240  * Create a new TimeField
19241  * @param {Object} config The config object
19242  */
19243
19244 Roo.bootstrap.TimeField = function(config){
19245     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19246     this.addEvents({
19247             /**
19248              * @event show
19249              * Fires when this field show.
19250              * @param {Roo.bootstrap.DateField} thisthis
19251              * @param {Mixed} date The date value
19252              */
19253             show : true,
19254             /**
19255              * @event show
19256              * Fires when this field hide.
19257              * @param {Roo.bootstrap.DateField} this
19258              * @param {Mixed} date The date value
19259              */
19260             hide : true,
19261             /**
19262              * @event select
19263              * Fires when select a date.
19264              * @param {Roo.bootstrap.DateField} this
19265              * @param {Mixed} date The date value
19266              */
19267             select : true
19268         });
19269 };
19270
19271 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19272     
19273     /**
19274      * @cfg {String} format
19275      * The default time format string which can be overriden for localization support.  The format must be
19276      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19277      */
19278     format : "H:i",
19279        
19280     onRender: function(ct, position)
19281     {
19282         
19283         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19284                 
19285         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19286         
19287         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19288         
19289         this.pop = this.picker().select('>.datepicker-time',true).first();
19290         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19291         
19292         this.picker().on('mousedown', this.onMousedown, this);
19293         this.picker().on('click', this.onClick, this);
19294         
19295         this.picker().addClass('datepicker-dropdown');
19296     
19297         this.fillTime();
19298         this.update();
19299             
19300         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19301         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19302         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19303         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19304         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19305         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19306
19307     },
19308     
19309     fireKey: function(e){
19310         if (!this.picker().isVisible()){
19311             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19312                 this.show();
19313             }
19314             return;
19315         }
19316
19317         e.preventDefault();
19318         
19319         switch(e.keyCode){
19320             case 27: // escape
19321                 this.hide();
19322                 break;
19323             case 37: // left
19324             case 39: // right
19325                 this.onTogglePeriod();
19326                 break;
19327             case 38: // up
19328                 this.onIncrementMinutes();
19329                 break;
19330             case 40: // down
19331                 this.onDecrementMinutes();
19332                 break;
19333             case 13: // enter
19334             case 9: // tab
19335                 this.setTime();
19336                 break;
19337         }
19338     },
19339     
19340     onClick: function(e) {
19341         e.stopPropagation();
19342         e.preventDefault();
19343     },
19344     
19345     picker : function()
19346     {
19347         return this.el.select('.datepicker', true).first();
19348     },
19349     
19350     fillTime: function()
19351     {    
19352         var time = this.pop.select('tbody', true).first();
19353         
19354         time.dom.innerHTML = '';
19355         
19356         time.createChild({
19357             tag: 'tr',
19358             cn: [
19359                 {
19360                     tag: 'td',
19361                     cn: [
19362                         {
19363                             tag: 'a',
19364                             href: '#',
19365                             cls: 'btn',
19366                             cn: [
19367                                 {
19368                                     tag: 'span',
19369                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19370                                 }
19371                             ]
19372                         } 
19373                     ]
19374                 },
19375                 {
19376                     tag: 'td',
19377                     cls: 'separator'
19378                 },
19379                 {
19380                     tag: 'td',
19381                     cn: [
19382                         {
19383                             tag: 'a',
19384                             href: '#',
19385                             cls: 'btn',
19386                             cn: [
19387                                 {
19388                                     tag: 'span',
19389                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19390                                 }
19391                             ]
19392                         }
19393                     ]
19394                 },
19395                 {
19396                     tag: 'td',
19397                     cls: 'separator'
19398                 }
19399             ]
19400         });
19401         
19402         time.createChild({
19403             tag: 'tr',
19404             cn: [
19405                 {
19406                     tag: 'td',
19407                     cn: [
19408                         {
19409                             tag: 'span',
19410                             cls: 'timepicker-hour',
19411                             html: '00'
19412                         }  
19413                     ]
19414                 },
19415                 {
19416                     tag: 'td',
19417                     cls: 'separator',
19418                     html: ':'
19419                 },
19420                 {
19421                     tag: 'td',
19422                     cn: [
19423                         {
19424                             tag: 'span',
19425                             cls: 'timepicker-minute',
19426                             html: '00'
19427                         }  
19428                     ]
19429                 },
19430                 {
19431                     tag: 'td',
19432                     cls: 'separator'
19433                 },
19434                 {
19435                     tag: 'td',
19436                     cn: [
19437                         {
19438                             tag: 'button',
19439                             type: 'button',
19440                             cls: 'btn btn-primary period',
19441                             html: 'AM'
19442                             
19443                         }
19444                     ]
19445                 }
19446             ]
19447         });
19448         
19449         time.createChild({
19450             tag: 'tr',
19451             cn: [
19452                 {
19453                     tag: 'td',
19454                     cn: [
19455                         {
19456                             tag: 'a',
19457                             href: '#',
19458                             cls: 'btn',
19459                             cn: [
19460                                 {
19461                                     tag: 'span',
19462                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19463                                 }
19464                             ]
19465                         }
19466                     ]
19467                 },
19468                 {
19469                     tag: 'td',
19470                     cls: 'separator'
19471                 },
19472                 {
19473                     tag: 'td',
19474                     cn: [
19475                         {
19476                             tag: 'a',
19477                             href: '#',
19478                             cls: 'btn',
19479                             cn: [
19480                                 {
19481                                     tag: 'span',
19482                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19483                                 }
19484                             ]
19485                         }
19486                     ]
19487                 },
19488                 {
19489                     tag: 'td',
19490                     cls: 'separator'
19491                 }
19492             ]
19493         });
19494         
19495     },
19496     
19497     update: function()
19498     {
19499         
19500         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19501         
19502         this.fill();
19503     },
19504     
19505     fill: function() 
19506     {
19507         var hours = this.time.getHours();
19508         var minutes = this.time.getMinutes();
19509         var period = 'AM';
19510         
19511         if(hours > 11){
19512             period = 'PM';
19513         }
19514         
19515         if(hours == 0){
19516             hours = 12;
19517         }
19518         
19519         
19520         if(hours > 12){
19521             hours = hours - 12;
19522         }
19523         
19524         if(hours < 10){
19525             hours = '0' + hours;
19526         }
19527         
19528         if(minutes < 10){
19529             minutes = '0' + minutes;
19530         }
19531         
19532         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19533         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19534         this.pop.select('button', true).first().dom.innerHTML = period;
19535         
19536     },
19537     
19538     place: function()
19539     {   
19540         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19541         
19542         var cls = ['bottom'];
19543         
19544         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19545             cls.pop();
19546             cls.push('top');
19547         }
19548         
19549         cls.push('right');
19550         
19551         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19552             cls.pop();
19553             cls.push('left');
19554         }
19555         
19556         this.picker().addClass(cls.join('-'));
19557         
19558         var _this = this;
19559         
19560         Roo.each(cls, function(c){
19561             if(c == 'bottom'){
19562                 _this.picker().setTop(_this.inputEl().getHeight());
19563                 return;
19564             }
19565             if(c == 'top'){
19566                 _this.picker().setTop(0 - _this.picker().getHeight());
19567                 return;
19568             }
19569             
19570             if(c == 'left'){
19571                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19572                 return;
19573             }
19574             if(c == 'right'){
19575                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19576                 return;
19577             }
19578         });
19579         
19580     },
19581   
19582     onFocus : function()
19583     {
19584         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19585         this.show();
19586     },
19587     
19588     onBlur : function()
19589     {
19590         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19591         this.hide();
19592     },
19593     
19594     show : function()
19595     {
19596         this.picker().show();
19597         this.pop.show();
19598         this.update();
19599         this.place();
19600         
19601         this.fireEvent('show', this, this.date);
19602     },
19603     
19604     hide : function()
19605     {
19606         this.picker().hide();
19607         this.pop.hide();
19608         
19609         this.fireEvent('hide', this, this.date);
19610     },
19611     
19612     setTime : function()
19613     {
19614         this.hide();
19615         this.setValue(this.time.format(this.format));
19616         
19617         this.fireEvent('select', this, this.date);
19618         
19619         
19620     },
19621     
19622     onMousedown: function(e){
19623         e.stopPropagation();
19624         e.preventDefault();
19625     },
19626     
19627     onIncrementHours: function()
19628     {
19629         Roo.log('onIncrementHours');
19630         this.time = this.time.add(Date.HOUR, 1);
19631         this.update();
19632         
19633     },
19634     
19635     onDecrementHours: function()
19636     {
19637         Roo.log('onDecrementHours');
19638         this.time = this.time.add(Date.HOUR, -1);
19639         this.update();
19640     },
19641     
19642     onIncrementMinutes: function()
19643     {
19644         Roo.log('onIncrementMinutes');
19645         this.time = this.time.add(Date.MINUTE, 1);
19646         this.update();
19647     },
19648     
19649     onDecrementMinutes: function()
19650     {
19651         Roo.log('onDecrementMinutes');
19652         this.time = this.time.add(Date.MINUTE, -1);
19653         this.update();
19654     },
19655     
19656     onTogglePeriod: function()
19657     {
19658         Roo.log('onTogglePeriod');
19659         this.time = this.time.add(Date.HOUR, 12);
19660         this.update();
19661     }
19662     
19663    
19664 });
19665
19666 Roo.apply(Roo.bootstrap.TimeField,  {
19667     
19668     content : {
19669         tag: 'tbody',
19670         cn: [
19671             {
19672                 tag: 'tr',
19673                 cn: [
19674                 {
19675                     tag: 'td',
19676                     colspan: '7'
19677                 }
19678                 ]
19679             }
19680         ]
19681     },
19682     
19683     footer : {
19684         tag: 'tfoot',
19685         cn: [
19686             {
19687                 tag: 'tr',
19688                 cn: [
19689                 {
19690                     tag: 'th',
19691                     colspan: '7',
19692                     cls: '',
19693                     cn: [
19694                         {
19695                             tag: 'button',
19696                             cls: 'btn btn-info ok',
19697                             html: 'OK'
19698                         }
19699                     ]
19700                 }
19701
19702                 ]
19703             }
19704         ]
19705     }
19706 });
19707
19708 Roo.apply(Roo.bootstrap.TimeField,  {
19709   
19710     template : {
19711         tag: 'div',
19712         cls: 'datepicker dropdown-menu',
19713         cn: [
19714             {
19715                 tag: 'div',
19716                 cls: 'datepicker-time',
19717                 cn: [
19718                 {
19719                     tag: 'table',
19720                     cls: 'table-condensed',
19721                     cn:[
19722                     Roo.bootstrap.TimeField.content,
19723                     Roo.bootstrap.TimeField.footer
19724                     ]
19725                 }
19726                 ]
19727             }
19728         ]
19729     }
19730 });
19731
19732  
19733
19734  /*
19735  * - LGPL
19736  *
19737  * MonthField
19738  * 
19739  */
19740
19741 /**
19742  * @class Roo.bootstrap.MonthField
19743  * @extends Roo.bootstrap.Input
19744  * Bootstrap MonthField class
19745  * 
19746  * @cfg {String} language default en
19747  * 
19748  * @constructor
19749  * Create a new MonthField
19750  * @param {Object} config The config object
19751  */
19752
19753 Roo.bootstrap.MonthField = function(config){
19754     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19755     
19756     this.addEvents({
19757         /**
19758          * @event show
19759          * Fires when this field show.
19760          * @param {Roo.bootstrap.MonthField} this
19761          * @param {Mixed} date The date value
19762          */
19763         show : true,
19764         /**
19765          * @event show
19766          * Fires when this field hide.
19767          * @param {Roo.bootstrap.MonthField} this
19768          * @param {Mixed} date The date value
19769          */
19770         hide : true,
19771         /**
19772          * @event select
19773          * Fires when select a date.
19774          * @param {Roo.bootstrap.MonthField} this
19775          * @param {String} oldvalue The old value
19776          * @param {String} newvalue The new value
19777          */
19778         select : true
19779     });
19780 };
19781
19782 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19783     
19784     onRender: function(ct, position)
19785     {
19786         
19787         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19788         
19789         this.language = this.language || 'en';
19790         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19791         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19792         
19793         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19794         this.isInline = false;
19795         this.isInput = true;
19796         this.component = this.el.select('.add-on', true).first() || false;
19797         this.component = (this.component && this.component.length === 0) ? false : this.component;
19798         this.hasInput = this.component && this.inputEL().length;
19799         
19800         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19801         
19802         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19803         
19804         this.picker().on('mousedown', this.onMousedown, this);
19805         this.picker().on('click', this.onClick, this);
19806         
19807         this.picker().addClass('datepicker-dropdown');
19808         
19809         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19810             v.setStyle('width', '189px');
19811         });
19812         
19813         this.fillMonths();
19814         
19815         this.update();
19816         
19817         if(this.isInline) {
19818             this.show();
19819         }
19820         
19821     },
19822     
19823     setValue: function(v, suppressEvent)
19824     {   
19825         var o = this.getValue();
19826         
19827         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19828         
19829         this.update();
19830
19831         if(suppressEvent !== true){
19832             this.fireEvent('select', this, o, v);
19833         }
19834         
19835     },
19836     
19837     getValue: function()
19838     {
19839         return this.value;
19840     },
19841     
19842     onClick: function(e) 
19843     {
19844         e.stopPropagation();
19845         e.preventDefault();
19846         
19847         var target = e.getTarget();
19848         
19849         if(target.nodeName.toLowerCase() === 'i'){
19850             target = Roo.get(target).dom.parentNode;
19851         }
19852         
19853         var nodeName = target.nodeName;
19854         var className = target.className;
19855         var html = target.innerHTML;
19856         
19857         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19858             return;
19859         }
19860         
19861         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19862         
19863         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19864         
19865         this.hide();
19866                         
19867     },
19868     
19869     picker : function()
19870     {
19871         return this.pickerEl;
19872     },
19873     
19874     fillMonths: function()
19875     {    
19876         var i = 0;
19877         var months = this.picker().select('>.datepicker-months td', true).first();
19878         
19879         months.dom.innerHTML = '';
19880         
19881         while (i < 12) {
19882             var month = {
19883                 tag: 'span',
19884                 cls: 'month',
19885                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19886             };
19887             
19888             months.createChild(month);
19889         }
19890         
19891     },
19892     
19893     update: function()
19894     {
19895         var _this = this;
19896         
19897         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19898             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19899         }
19900         
19901         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19902             e.removeClass('active');
19903             
19904             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19905                 e.addClass('active');
19906             }
19907         })
19908     },
19909     
19910     place: function()
19911     {
19912         if(this.isInline) {
19913             return;
19914         }
19915         
19916         this.picker().removeClass(['bottom', 'top']);
19917         
19918         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19919             /*
19920              * place to the top of element!
19921              *
19922              */
19923             
19924             this.picker().addClass('top');
19925             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19926             
19927             return;
19928         }
19929         
19930         this.picker().addClass('bottom');
19931         
19932         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19933     },
19934     
19935     onFocus : function()
19936     {
19937         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19938         this.show();
19939     },
19940     
19941     onBlur : function()
19942     {
19943         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19944         
19945         var d = this.inputEl().getValue();
19946         
19947         this.setValue(d);
19948                 
19949         this.hide();
19950     },
19951     
19952     show : function()
19953     {
19954         this.picker().show();
19955         this.picker().select('>.datepicker-months', true).first().show();
19956         this.update();
19957         this.place();
19958         
19959         this.fireEvent('show', this, this.date);
19960     },
19961     
19962     hide : function()
19963     {
19964         if(this.isInline) {
19965             return;
19966         }
19967         this.picker().hide();
19968         this.fireEvent('hide', this, this.date);
19969         
19970     },
19971     
19972     onMousedown: function(e)
19973     {
19974         e.stopPropagation();
19975         e.preventDefault();
19976     },
19977     
19978     keyup: function(e)
19979     {
19980         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19981         this.update();
19982     },
19983
19984     fireKey: function(e)
19985     {
19986         if (!this.picker().isVisible()){
19987             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19988                 this.show();
19989             }
19990             return;
19991         }
19992         
19993         var dir;
19994         
19995         switch(e.keyCode){
19996             case 27: // escape
19997                 this.hide();
19998                 e.preventDefault();
19999                 break;
20000             case 37: // left
20001             case 39: // right
20002                 dir = e.keyCode == 37 ? -1 : 1;
20003                 
20004                 this.vIndex = this.vIndex + dir;
20005                 
20006                 if(this.vIndex < 0){
20007                     this.vIndex = 0;
20008                 }
20009                 
20010                 if(this.vIndex > 11){
20011                     this.vIndex = 11;
20012                 }
20013                 
20014                 if(isNaN(this.vIndex)){
20015                     this.vIndex = 0;
20016                 }
20017                 
20018                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20019                 
20020                 break;
20021             case 38: // up
20022             case 40: // down
20023                 
20024                 dir = e.keyCode == 38 ? -1 : 1;
20025                 
20026                 this.vIndex = this.vIndex + dir * 4;
20027                 
20028                 if(this.vIndex < 0){
20029                     this.vIndex = 0;
20030                 }
20031                 
20032                 if(this.vIndex > 11){
20033                     this.vIndex = 11;
20034                 }
20035                 
20036                 if(isNaN(this.vIndex)){
20037                     this.vIndex = 0;
20038                 }
20039                 
20040                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20041                 break;
20042                 
20043             case 13: // enter
20044                 
20045                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20046                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20047                 }
20048                 
20049                 this.hide();
20050                 e.preventDefault();
20051                 break;
20052             case 9: // tab
20053                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20054                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20055                 }
20056                 this.hide();
20057                 break;
20058             case 16: // shift
20059             case 17: // ctrl
20060             case 18: // alt
20061                 break;
20062             default :
20063                 this.hide();
20064                 
20065         }
20066     },
20067     
20068     remove: function() 
20069     {
20070         this.picker().remove();
20071     }
20072    
20073 });
20074
20075 Roo.apply(Roo.bootstrap.MonthField,  {
20076     
20077     content : {
20078         tag: 'tbody',
20079         cn: [
20080         {
20081             tag: 'tr',
20082             cn: [
20083             {
20084                 tag: 'td',
20085                 colspan: '7'
20086             }
20087             ]
20088         }
20089         ]
20090     },
20091     
20092     dates:{
20093         en: {
20094             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20095             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20096         }
20097     }
20098 });
20099
20100 Roo.apply(Roo.bootstrap.MonthField,  {
20101   
20102     template : {
20103         tag: 'div',
20104         cls: 'datepicker dropdown-menu roo-dynamic',
20105         cn: [
20106             {
20107                 tag: 'div',
20108                 cls: 'datepicker-months',
20109                 cn: [
20110                 {
20111                     tag: 'table',
20112                     cls: 'table-condensed',
20113                     cn:[
20114                         Roo.bootstrap.DateField.content
20115                     ]
20116                 }
20117                 ]
20118             }
20119         ]
20120     }
20121 });
20122
20123  
20124
20125  
20126  /*
20127  * - LGPL
20128  *
20129  * CheckBox
20130  * 
20131  */
20132
20133 /**
20134  * @class Roo.bootstrap.CheckBox
20135  * @extends Roo.bootstrap.Input
20136  * Bootstrap CheckBox class
20137  * 
20138  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20139  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20140  * @cfg {String} boxLabel The text that appears beside the checkbox
20141  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20142  * @cfg {Boolean} checked initnal the element
20143  * @cfg {Boolean} inline inline the element (default false)
20144  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20145  * @cfg {String} tooltip label tooltip
20146  * 
20147  * @constructor
20148  * Create a new CheckBox
20149  * @param {Object} config The config object
20150  */
20151
20152 Roo.bootstrap.CheckBox = function(config){
20153     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20154    
20155     this.addEvents({
20156         /**
20157         * @event check
20158         * Fires when the element is checked or unchecked.
20159         * @param {Roo.bootstrap.CheckBox} this This input
20160         * @param {Boolean} checked The new checked value
20161         */
20162        check : true,
20163        /**
20164         * @event click
20165         * Fires when the element is click.
20166         * @param {Roo.bootstrap.CheckBox} this This input
20167         */
20168        click : true
20169     });
20170     
20171 };
20172
20173 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20174   
20175     inputType: 'checkbox',
20176     inputValue: 1,
20177     valueOff: 0,
20178     boxLabel: false,
20179     checked: false,
20180     weight : false,
20181     inline: false,
20182     tooltip : '',
20183     
20184     getAutoCreate : function()
20185     {
20186         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20187         
20188         var id = Roo.id();
20189         
20190         var cfg = {};
20191         
20192         cfg.cls = 'form-group ' + this.inputType; //input-group
20193         
20194         if(this.inline){
20195             cfg.cls += ' ' + this.inputType + '-inline';
20196         }
20197         
20198         var input =  {
20199             tag: 'input',
20200             id : id,
20201             type : this.inputType,
20202             value : this.inputValue,
20203             cls : 'roo-' + this.inputType, //'form-box',
20204             placeholder : this.placeholder || ''
20205             
20206         };
20207         
20208         if(this.inputType != 'radio'){
20209             var hidden =  {
20210                 tag: 'input',
20211                 type : 'hidden',
20212                 cls : 'roo-hidden-value',
20213                 value : this.checked ? this.inputValue : this.valueOff
20214             };
20215         }
20216         
20217             
20218         if (this.weight) { // Validity check?
20219             cfg.cls += " " + this.inputType + "-" + this.weight;
20220         }
20221         
20222         if (this.disabled) {
20223             input.disabled=true;
20224         }
20225         
20226         if(this.checked){
20227             input.checked = this.checked;
20228         }
20229         
20230         if (this.name) {
20231             
20232             input.name = this.name;
20233             
20234             if(this.inputType != 'radio'){
20235                 hidden.name = this.name;
20236                 input.name = '_hidden_' + this.name;
20237             }
20238         }
20239         
20240         if (this.size) {
20241             input.cls += ' input-' + this.size;
20242         }
20243         
20244         var settings=this;
20245         
20246         ['xs','sm','md','lg'].map(function(size){
20247             if (settings[size]) {
20248                 cfg.cls += ' col-' + size + '-' + settings[size];
20249             }
20250         });
20251         
20252         var inputblock = input;
20253          
20254         if (this.before || this.after) {
20255             
20256             inputblock = {
20257                 cls : 'input-group',
20258                 cn :  [] 
20259             };
20260             
20261             if (this.before) {
20262                 inputblock.cn.push({
20263                     tag :'span',
20264                     cls : 'input-group-addon',
20265                     html : this.before
20266                 });
20267             }
20268             
20269             inputblock.cn.push(input);
20270             
20271             if(this.inputType != 'radio'){
20272                 inputblock.cn.push(hidden);
20273             }
20274             
20275             if (this.after) {
20276                 inputblock.cn.push({
20277                     tag :'span',
20278                     cls : 'input-group-addon',
20279                     html : this.after
20280                 });
20281             }
20282             
20283         }
20284         
20285         if (align ==='left' && this.fieldLabel.length) {
20286 //                Roo.log("left and has label");
20287             cfg.cn = [
20288                 {
20289                     tag: 'label',
20290                     'for' :  id,
20291                     cls : 'control-label',
20292                     html : this.fieldLabel
20293                 },
20294                 {
20295                     cls : "", 
20296                     cn: [
20297                         inputblock
20298                     ]
20299                 }
20300             ];
20301             
20302             if(this.labelWidth > 12){
20303                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20304             }
20305             
20306             if(this.labelWidth < 13 && this.labelmd == 0){
20307                 this.labelmd = this.labelWidth;
20308             }
20309             
20310             if(this.labellg > 0){
20311                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20312                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20313             }
20314             
20315             if(this.labelmd > 0){
20316                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20317                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20318             }
20319             
20320             if(this.labelsm > 0){
20321                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20322                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20323             }
20324             
20325             if(this.labelxs > 0){
20326                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20327                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20328             }
20329             
20330         } else if ( this.fieldLabel.length) {
20331 //                Roo.log(" label");
20332                 cfg.cn = [
20333                    
20334                     {
20335                         tag: this.boxLabel ? 'span' : 'label',
20336                         'for': id,
20337                         cls: 'control-label box-input-label',
20338                         //cls : 'input-group-addon',
20339                         html : this.fieldLabel
20340                     },
20341                     
20342                     inputblock
20343                     
20344                 ];
20345
20346         } else {
20347             
20348 //                Roo.log(" no label && no align");
20349                 cfg.cn = [  inputblock ] ;
20350                 
20351                 
20352         }
20353         
20354         if(this.boxLabel){
20355              var boxLabelCfg = {
20356                 tag: 'label',
20357                 //'for': id, // box label is handled by onclick - so no for...
20358                 cls: 'box-label',
20359                 html: this.boxLabel
20360             };
20361             
20362             if(this.tooltip){
20363                 boxLabelCfg.tooltip = this.tooltip;
20364             }
20365              
20366             cfg.cn.push(boxLabelCfg);
20367         }
20368         
20369         if(this.inputType != 'radio'){
20370             cfg.cn.push(hidden);
20371         }
20372         
20373         return cfg;
20374         
20375     },
20376     
20377     /**
20378      * return the real input element.
20379      */
20380     inputEl: function ()
20381     {
20382         return this.el.select('input.roo-' + this.inputType,true).first();
20383     },
20384     hiddenEl: function ()
20385     {
20386         return this.el.select('input.roo-hidden-value',true).first();
20387     },
20388     
20389     labelEl: function()
20390     {
20391         return this.el.select('label.control-label',true).first();
20392     },
20393     /* depricated... */
20394     
20395     label: function()
20396     {
20397         return this.labelEl();
20398     },
20399     
20400     boxLabelEl: function()
20401     {
20402         return this.el.select('label.box-label',true).first();
20403     },
20404     
20405     initEvents : function()
20406     {
20407 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20408         
20409         this.inputEl().on('click', this.onClick,  this);
20410         
20411         if (this.boxLabel) { 
20412             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20413         }
20414         
20415         this.startValue = this.getValue();
20416         
20417         if(this.groupId){
20418             Roo.bootstrap.CheckBox.register(this);
20419         }
20420     },
20421     
20422     onClick : function(e)
20423     {   
20424         if(this.fireEvent('click', this, e) !== false){
20425             this.setChecked(!this.checked);
20426         }
20427         
20428     },
20429     
20430     setChecked : function(state,suppressEvent)
20431     {
20432         this.startValue = this.getValue();
20433
20434         if(this.inputType == 'radio'){
20435             
20436             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20437                 e.dom.checked = false;
20438             });
20439             
20440             this.inputEl().dom.checked = true;
20441             
20442             this.inputEl().dom.value = this.inputValue;
20443             
20444             if(suppressEvent !== true){
20445                 this.fireEvent('check', this, true);
20446             }
20447             
20448             this.validate();
20449             
20450             return;
20451         }
20452         
20453         this.checked = state;
20454         
20455         this.inputEl().dom.checked = state;
20456         
20457         
20458         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20459         
20460         if(suppressEvent !== true){
20461             this.fireEvent('check', this, state);
20462         }
20463         
20464         this.validate();
20465     },
20466     
20467     getValue : function()
20468     {
20469         if(this.inputType == 'radio'){
20470             return this.getGroupValue();
20471         }
20472         
20473         return this.hiddenEl().dom.value;
20474         
20475     },
20476     
20477     getGroupValue : function()
20478     {
20479         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20480             return '';
20481         }
20482         
20483         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20484     },
20485     
20486     setValue : function(v,suppressEvent)
20487     {
20488         if(this.inputType == 'radio'){
20489             this.setGroupValue(v, suppressEvent);
20490             return;
20491         }
20492         
20493         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20494         
20495         this.validate();
20496     },
20497     
20498     setGroupValue : function(v, suppressEvent)
20499     {
20500         this.startValue = this.getValue();
20501         
20502         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20503             e.dom.checked = false;
20504             
20505             if(e.dom.value == v){
20506                 e.dom.checked = true;
20507             }
20508         });
20509         
20510         if(suppressEvent !== true){
20511             this.fireEvent('check', this, true);
20512         }
20513
20514         this.validate();
20515         
20516         return;
20517     },
20518     
20519     validate : function()
20520     {
20521         if(
20522                 this.disabled || 
20523                 (this.inputType == 'radio' && this.validateRadio()) ||
20524                 (this.inputType == 'checkbox' && this.validateCheckbox())
20525         ){
20526             this.markValid();
20527             return true;
20528         }
20529         
20530         this.markInvalid();
20531         return false;
20532     },
20533     
20534     validateRadio : function()
20535     {
20536         if(this.allowBlank){
20537             return true;
20538         }
20539         
20540         var valid = false;
20541         
20542         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20543             if(!e.dom.checked){
20544                 return;
20545             }
20546             
20547             valid = true;
20548             
20549             return false;
20550         });
20551         
20552         return valid;
20553     },
20554     
20555     validateCheckbox : function()
20556     {
20557         if(!this.groupId){
20558             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20559             //return (this.getValue() == this.inputValue) ? true : false;
20560         }
20561         
20562         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20563         
20564         if(!group){
20565             return false;
20566         }
20567         
20568         var r = false;
20569         
20570         for(var i in group){
20571             if(group[i].el.isVisible(true)){
20572                 r = false;
20573                 break;
20574             }
20575             
20576             r = true;
20577         }
20578         
20579         for(var i in group){
20580             if(r){
20581                 break;
20582             }
20583             
20584             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20585         }
20586         
20587         return r;
20588     },
20589     
20590     /**
20591      * Mark this field as valid
20592      */
20593     markValid : function()
20594     {
20595         var _this = this;
20596         
20597         this.fireEvent('valid', this);
20598         
20599         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20600         
20601         if(this.groupId){
20602             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20603         }
20604         
20605         if(label){
20606             label.markValid();
20607         }
20608
20609         if(this.inputType == 'radio'){
20610             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20611                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20612                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20613             });
20614             
20615             return;
20616         }
20617
20618         if(!this.groupId){
20619             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20620             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20621             return;
20622         }
20623         
20624         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20625         
20626         if(!group){
20627             return;
20628         }
20629         
20630         for(var i in group){
20631             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20632             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20633         }
20634     },
20635     
20636      /**
20637      * Mark this field as invalid
20638      * @param {String} msg The validation message
20639      */
20640     markInvalid : function(msg)
20641     {
20642         if(this.allowBlank){
20643             return;
20644         }
20645         
20646         var _this = this;
20647         
20648         this.fireEvent('invalid', this, msg);
20649         
20650         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20651         
20652         if(this.groupId){
20653             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20654         }
20655         
20656         if(label){
20657             label.markInvalid();
20658         }
20659             
20660         if(this.inputType == 'radio'){
20661             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20662                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20663                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20664             });
20665             
20666             return;
20667         }
20668         
20669         if(!this.groupId){
20670             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20671             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20672             return;
20673         }
20674         
20675         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20676         
20677         if(!group){
20678             return;
20679         }
20680         
20681         for(var i in group){
20682             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20683             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20684         }
20685         
20686     },
20687     
20688     clearInvalid : function()
20689     {
20690         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20691         
20692         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20693         
20694         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20695         
20696         if (label && label.iconEl) {
20697             label.iconEl.removeClass(label.validClass);
20698             label.iconEl.removeClass(label.invalidClass);
20699         }
20700     },
20701     
20702     disable : function()
20703     {
20704         if(this.inputType != 'radio'){
20705             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20706             return;
20707         }
20708         
20709         var _this = this;
20710         
20711         if(this.rendered){
20712             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20713                 _this.getActionEl().addClass(this.disabledClass);
20714                 e.dom.disabled = true;
20715             });
20716         }
20717         
20718         this.disabled = true;
20719         this.fireEvent("disable", this);
20720         return this;
20721     },
20722
20723     enable : function()
20724     {
20725         if(this.inputType != 'radio'){
20726             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20727             return;
20728         }
20729         
20730         var _this = this;
20731         
20732         if(this.rendered){
20733             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20734                 _this.getActionEl().removeClass(this.disabledClass);
20735                 e.dom.disabled = false;
20736             });
20737         }
20738         
20739         this.disabled = false;
20740         this.fireEvent("enable", this);
20741         return this;
20742     }
20743
20744 });
20745
20746 Roo.apply(Roo.bootstrap.CheckBox, {
20747     
20748     groups: {},
20749     
20750      /**
20751     * register a CheckBox Group
20752     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20753     */
20754     register : function(checkbox)
20755     {
20756         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20757             this.groups[checkbox.groupId] = {};
20758         }
20759         
20760         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20761             return;
20762         }
20763         
20764         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20765         
20766     },
20767     /**
20768     * fetch a CheckBox Group based on the group ID
20769     * @param {string} the group ID
20770     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20771     */
20772     get: function(groupId) {
20773         if (typeof(this.groups[groupId]) == 'undefined') {
20774             return false;
20775         }
20776         
20777         return this.groups[groupId] ;
20778     }
20779     
20780     
20781 });
20782 /*
20783  * - LGPL
20784  *
20785  * RadioItem
20786  * 
20787  */
20788
20789 /**
20790  * @class Roo.bootstrap.Radio
20791  * @extends Roo.bootstrap.Component
20792  * Bootstrap Radio class
20793  * @cfg {String} boxLabel - the label associated
20794  * @cfg {String} value - the value of radio
20795  * 
20796  * @constructor
20797  * Create a new Radio
20798  * @param {Object} config The config object
20799  */
20800 Roo.bootstrap.Radio = function(config){
20801     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20802     
20803 };
20804
20805 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20806     
20807     boxLabel : '',
20808     
20809     value : '',
20810     
20811     getAutoCreate : function()
20812     {
20813         var cfg = {
20814             tag : 'div',
20815             cls : 'form-group radio',
20816             cn : [
20817                 {
20818                     tag : 'label',
20819                     cls : 'box-label',
20820                     html : this.boxLabel
20821                 }
20822             ]
20823         };
20824         
20825         return cfg;
20826     },
20827     
20828     initEvents : function() 
20829     {
20830         this.parent().register(this);
20831         
20832         this.el.on('click', this.onClick, this);
20833         
20834     },
20835     
20836     onClick : function()
20837     {
20838         this.setChecked(true);
20839     },
20840     
20841     setChecked : function(state, suppressEvent)
20842     {
20843         this.parent().setValue(this.value, suppressEvent);
20844         
20845     },
20846     
20847     setBoxLabel : function(v)
20848     {
20849         this.boxLabel = v;
20850         
20851         if(this.rendered){
20852             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20853         }
20854     }
20855     
20856 });
20857  
20858
20859  /*
20860  * - LGPL
20861  *
20862  * Input
20863  * 
20864  */
20865
20866 /**
20867  * @class Roo.bootstrap.SecurePass
20868  * @extends Roo.bootstrap.Input
20869  * Bootstrap SecurePass class
20870  *
20871  * 
20872  * @constructor
20873  * Create a new SecurePass
20874  * @param {Object} config The config object
20875  */
20876  
20877 Roo.bootstrap.SecurePass = function (config) {
20878     // these go here, so the translation tool can replace them..
20879     this.errors = {
20880         PwdEmpty: "Please type a password, and then retype it to confirm.",
20881         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20882         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20883         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20884         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20885         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20886         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20887         TooWeak: "Your password is Too Weak."
20888     },
20889     this.meterLabel = "Password strength:";
20890     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20891     this.meterClass = [
20892         "roo-password-meter-tooweak", 
20893         "roo-password-meter-weak", 
20894         "roo-password-meter-medium", 
20895         "roo-password-meter-strong", 
20896         "roo-password-meter-grey"
20897     ];
20898     
20899     this.errors = {};
20900     
20901     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20902 }
20903
20904 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20905     /**
20906      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20907      * {
20908      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20909      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20910      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20911      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20912      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20913      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20914      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20915      * })
20916      */
20917     // private
20918     
20919     meterWidth: 300,
20920     errorMsg :'',    
20921     errors: false,
20922     imageRoot: '/',
20923     /**
20924      * @cfg {String/Object} Label for the strength meter (defaults to
20925      * 'Password strength:')
20926      */
20927     // private
20928     meterLabel: '',
20929     /**
20930      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20931      * ['Weak', 'Medium', 'Strong'])
20932      */
20933     // private    
20934     pwdStrengths: false,    
20935     // private
20936     strength: 0,
20937     // private
20938     _lastPwd: null,
20939     // private
20940     kCapitalLetter: 0,
20941     kSmallLetter: 1,
20942     kDigit: 2,
20943     kPunctuation: 3,
20944     
20945     insecure: false,
20946     // private
20947     initEvents: function ()
20948     {
20949         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20950
20951         if (this.el.is('input[type=password]') && Roo.isSafari) {
20952             this.el.on('keydown', this.SafariOnKeyDown, this);
20953         }
20954
20955         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20956     },
20957     // private
20958     onRender: function (ct, position)
20959     {
20960         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20961         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20962         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20963
20964         this.trigger.createChild({
20965                    cn: [
20966                     {
20967                     //id: 'PwdMeter',
20968                     tag: 'div',
20969                     cls: 'roo-password-meter-grey col-xs-12',
20970                     style: {
20971                         //width: 0,
20972                         //width: this.meterWidth + 'px'                                                
20973                         }
20974                     },
20975                     {                            
20976                          cls: 'roo-password-meter-text'                          
20977                     }
20978                 ]            
20979         });
20980
20981          
20982         if (this.hideTrigger) {
20983             this.trigger.setDisplayed(false);
20984         }
20985         this.setSize(this.width || '', this.height || '');
20986     },
20987     // private
20988     onDestroy: function ()
20989     {
20990         if (this.trigger) {
20991             this.trigger.removeAllListeners();
20992             this.trigger.remove();
20993         }
20994         if (this.wrap) {
20995             this.wrap.remove();
20996         }
20997         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20998     },
20999     // private
21000     checkStrength: function ()
21001     {
21002         var pwd = this.inputEl().getValue();
21003         if (pwd == this._lastPwd) {
21004             return;
21005         }
21006
21007         var strength;
21008         if (this.ClientSideStrongPassword(pwd)) {
21009             strength = 3;
21010         } else if (this.ClientSideMediumPassword(pwd)) {
21011             strength = 2;
21012         } else if (this.ClientSideWeakPassword(pwd)) {
21013             strength = 1;
21014         } else {
21015             strength = 0;
21016         }
21017         
21018         Roo.log('strength1: ' + strength);
21019         
21020         //var pm = this.trigger.child('div/div/div').dom;
21021         var pm = this.trigger.child('div/div');
21022         pm.removeClass(this.meterClass);
21023         pm.addClass(this.meterClass[strength]);
21024                 
21025         
21026         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21027                 
21028         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21029         
21030         this._lastPwd = pwd;
21031     },
21032     reset: function ()
21033     {
21034         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21035         
21036         this._lastPwd = '';
21037         
21038         var pm = this.trigger.child('div/div');
21039         pm.removeClass(this.meterClass);
21040         pm.addClass('roo-password-meter-grey');        
21041         
21042         
21043         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21044         
21045         pt.innerHTML = '';
21046         this.inputEl().dom.type='password';
21047     },
21048     // private
21049     validateValue: function (value)
21050     {
21051         
21052         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21053             return false;
21054         }
21055         if (value.length == 0) {
21056             if (this.allowBlank) {
21057                 this.clearInvalid();
21058                 return true;
21059             }
21060
21061             this.markInvalid(this.errors.PwdEmpty);
21062             this.errorMsg = this.errors.PwdEmpty;
21063             return false;
21064         }
21065         
21066         if(this.insecure){
21067             return true;
21068         }
21069         
21070         if ('[\x21-\x7e]*'.match(value)) {
21071             this.markInvalid(this.errors.PwdBadChar);
21072             this.errorMsg = this.errors.PwdBadChar;
21073             return false;
21074         }
21075         if (value.length < 6) {
21076             this.markInvalid(this.errors.PwdShort);
21077             this.errorMsg = this.errors.PwdShort;
21078             return false;
21079         }
21080         if (value.length > 16) {
21081             this.markInvalid(this.errors.PwdLong);
21082             this.errorMsg = this.errors.PwdLong;
21083             return false;
21084         }
21085         var strength;
21086         if (this.ClientSideStrongPassword(value)) {
21087             strength = 3;
21088         } else if (this.ClientSideMediumPassword(value)) {
21089             strength = 2;
21090         } else if (this.ClientSideWeakPassword(value)) {
21091             strength = 1;
21092         } else {
21093             strength = 0;
21094         }
21095
21096         
21097         if (strength < 2) {
21098             //this.markInvalid(this.errors.TooWeak);
21099             this.errorMsg = this.errors.TooWeak;
21100             //return false;
21101         }
21102         
21103         
21104         console.log('strength2: ' + strength);
21105         
21106         //var pm = this.trigger.child('div/div/div').dom;
21107         
21108         var pm = this.trigger.child('div/div');
21109         pm.removeClass(this.meterClass);
21110         pm.addClass(this.meterClass[strength]);
21111                 
21112         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21113                 
21114         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21115         
21116         this.errorMsg = ''; 
21117         return true;
21118     },
21119     // private
21120     CharacterSetChecks: function (type)
21121     {
21122         this.type = type;
21123         this.fResult = false;
21124     },
21125     // private
21126     isctype: function (character, type)
21127     {
21128         switch (type) {  
21129             case this.kCapitalLetter:
21130                 if (character >= 'A' && character <= 'Z') {
21131                     return true;
21132                 }
21133                 break;
21134             
21135             case this.kSmallLetter:
21136                 if (character >= 'a' && character <= 'z') {
21137                     return true;
21138                 }
21139                 break;
21140             
21141             case this.kDigit:
21142                 if (character >= '0' && character <= '9') {
21143                     return true;
21144                 }
21145                 break;
21146             
21147             case this.kPunctuation:
21148                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21149                     return true;
21150                 }
21151                 break;
21152             
21153             default:
21154                 return false;
21155         }
21156
21157     },
21158     // private
21159     IsLongEnough: function (pwd, size)
21160     {
21161         return !(pwd == null || isNaN(size) || pwd.length < size);
21162     },
21163     // private
21164     SpansEnoughCharacterSets: function (word, nb)
21165     {
21166         if (!this.IsLongEnough(word, nb))
21167         {
21168             return false;
21169         }
21170
21171         var characterSetChecks = new Array(
21172             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21173             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21174         );
21175         
21176         for (var index = 0; index < word.length; ++index) {
21177             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21178                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21179                     characterSetChecks[nCharSet].fResult = true;
21180                     break;
21181                 }
21182             }
21183         }
21184
21185         var nCharSets = 0;
21186         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21187             if (characterSetChecks[nCharSet].fResult) {
21188                 ++nCharSets;
21189             }
21190         }
21191
21192         if (nCharSets < nb) {
21193             return false;
21194         }
21195         return true;
21196     },
21197     // private
21198     ClientSideStrongPassword: function (pwd)
21199     {
21200         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21201     },
21202     // private
21203     ClientSideMediumPassword: function (pwd)
21204     {
21205         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21206     },
21207     // private
21208     ClientSideWeakPassword: function (pwd)
21209     {
21210         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21211     }
21212           
21213 })//<script type="text/javascript">
21214
21215 /*
21216  * Based  Ext JS Library 1.1.1
21217  * Copyright(c) 2006-2007, Ext JS, LLC.
21218  * LGPL
21219  *
21220  */
21221  
21222 /**
21223  * @class Roo.HtmlEditorCore
21224  * @extends Roo.Component
21225  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21226  *
21227  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21228  */
21229
21230 Roo.HtmlEditorCore = function(config){
21231     
21232     
21233     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21234     
21235     
21236     this.addEvents({
21237         /**
21238          * @event initialize
21239          * Fires when the editor is fully initialized (including the iframe)
21240          * @param {Roo.HtmlEditorCore} this
21241          */
21242         initialize: true,
21243         /**
21244          * @event activate
21245          * Fires when the editor is first receives the focus. Any insertion must wait
21246          * until after this event.
21247          * @param {Roo.HtmlEditorCore} this
21248          */
21249         activate: true,
21250          /**
21251          * @event beforesync
21252          * Fires before the textarea is updated with content from the editor iframe. Return false
21253          * to cancel the sync.
21254          * @param {Roo.HtmlEditorCore} this
21255          * @param {String} html
21256          */
21257         beforesync: true,
21258          /**
21259          * @event beforepush
21260          * Fires before the iframe editor is updated with content from the textarea. Return false
21261          * to cancel the push.
21262          * @param {Roo.HtmlEditorCore} this
21263          * @param {String} html
21264          */
21265         beforepush: true,
21266          /**
21267          * @event sync
21268          * Fires when the textarea is updated with content from the editor iframe.
21269          * @param {Roo.HtmlEditorCore} this
21270          * @param {String} html
21271          */
21272         sync: true,
21273          /**
21274          * @event push
21275          * Fires when the iframe editor is updated with content from the textarea.
21276          * @param {Roo.HtmlEditorCore} this
21277          * @param {String} html
21278          */
21279         push: true,
21280         
21281         /**
21282          * @event editorevent
21283          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21284          * @param {Roo.HtmlEditorCore} this
21285          */
21286         editorevent: true
21287         
21288     });
21289     
21290     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21291     
21292     // defaults : white / black...
21293     this.applyBlacklists();
21294     
21295     
21296     
21297 };
21298
21299
21300 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21301
21302
21303      /**
21304      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21305      */
21306     
21307     owner : false,
21308     
21309      /**
21310      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21311      *                        Roo.resizable.
21312      */
21313     resizable : false,
21314      /**
21315      * @cfg {Number} height (in pixels)
21316      */   
21317     height: 300,
21318    /**
21319      * @cfg {Number} width (in pixels)
21320      */   
21321     width: 500,
21322     
21323     /**
21324      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21325      * 
21326      */
21327     stylesheets: false,
21328     
21329     // id of frame..
21330     frameId: false,
21331     
21332     // private properties
21333     validationEvent : false,
21334     deferHeight: true,
21335     initialized : false,
21336     activated : false,
21337     sourceEditMode : false,
21338     onFocus : Roo.emptyFn,
21339     iframePad:3,
21340     hideMode:'offsets',
21341     
21342     clearUp: true,
21343     
21344     // blacklist + whitelisted elements..
21345     black: false,
21346     white: false,
21347      
21348     bodyCls : '',
21349
21350     /**
21351      * Protected method that will not generally be called directly. It
21352      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21353      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21354      */
21355     getDocMarkup : function(){
21356         // body styles..
21357         var st = '';
21358         
21359         // inherit styels from page...?? 
21360         if (this.stylesheets === false) {
21361             
21362             Roo.get(document.head).select('style').each(function(node) {
21363                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21364             });
21365             
21366             Roo.get(document.head).select('link').each(function(node) { 
21367                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21368             });
21369             
21370         } else if (!this.stylesheets.length) {
21371                 // simple..
21372                 st = '<style type="text/css">' +
21373                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21374                    '</style>';
21375         } else { 
21376             st = '<style type="text/css">' +
21377                     this.stylesheets +
21378                 '</style>';
21379         }
21380         
21381         st +=  '<style type="text/css">' +
21382             'IMG { cursor: pointer } ' +
21383         '</style>';
21384
21385         var cls = 'roo-htmleditor-body';
21386         
21387         if(this.bodyCls.length){
21388             cls += ' ' + this.bodyCls;
21389         }
21390         
21391         return '<html><head>' + st  +
21392             //<style type="text/css">' +
21393             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21394             //'</style>' +
21395             ' </head><body class="' +  cls + '"></body></html>';
21396     },
21397
21398     // private
21399     onRender : function(ct, position)
21400     {
21401         var _t = this;
21402         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21403         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21404         
21405         
21406         this.el.dom.style.border = '0 none';
21407         this.el.dom.setAttribute('tabIndex', -1);
21408         this.el.addClass('x-hidden hide');
21409         
21410         
21411         
21412         if(Roo.isIE){ // fix IE 1px bogus margin
21413             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21414         }
21415        
21416         
21417         this.frameId = Roo.id();
21418         
21419          
21420         
21421         var iframe = this.owner.wrap.createChild({
21422             tag: 'iframe',
21423             cls: 'form-control', // bootstrap..
21424             id: this.frameId,
21425             name: this.frameId,
21426             frameBorder : 'no',
21427             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21428         }, this.el
21429         );
21430         
21431         
21432         this.iframe = iframe.dom;
21433
21434          this.assignDocWin();
21435         
21436         this.doc.designMode = 'on';
21437        
21438         this.doc.open();
21439         this.doc.write(this.getDocMarkup());
21440         this.doc.close();
21441
21442         
21443         var task = { // must defer to wait for browser to be ready
21444             run : function(){
21445                 //console.log("run task?" + this.doc.readyState);
21446                 this.assignDocWin();
21447                 if(this.doc.body || this.doc.readyState == 'complete'){
21448                     try {
21449                         this.doc.designMode="on";
21450                     } catch (e) {
21451                         return;
21452                     }
21453                     Roo.TaskMgr.stop(task);
21454                     this.initEditor.defer(10, this);
21455                 }
21456             },
21457             interval : 10,
21458             duration: 10000,
21459             scope: this
21460         };
21461         Roo.TaskMgr.start(task);
21462
21463     },
21464
21465     // private
21466     onResize : function(w, h)
21467     {
21468          Roo.log('resize: ' +w + ',' + h );
21469         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21470         if(!this.iframe){
21471             return;
21472         }
21473         if(typeof w == 'number'){
21474             
21475             this.iframe.style.width = w + 'px';
21476         }
21477         if(typeof h == 'number'){
21478             
21479             this.iframe.style.height = h + 'px';
21480             if(this.doc){
21481                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21482             }
21483         }
21484         
21485     },
21486
21487     /**
21488      * Toggles the editor between standard and source edit mode.
21489      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21490      */
21491     toggleSourceEdit : function(sourceEditMode){
21492         
21493         this.sourceEditMode = sourceEditMode === true;
21494         
21495         if(this.sourceEditMode){
21496  
21497             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21498             
21499         }else{
21500             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21501             //this.iframe.className = '';
21502             this.deferFocus();
21503         }
21504         //this.setSize(this.owner.wrap.getSize());
21505         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21506     },
21507
21508     
21509   
21510
21511     /**
21512      * Protected method that will not generally be called directly. If you need/want
21513      * custom HTML cleanup, this is the method you should override.
21514      * @param {String} html The HTML to be cleaned
21515      * return {String} The cleaned HTML
21516      */
21517     cleanHtml : function(html){
21518         html = String(html);
21519         if(html.length > 5){
21520             if(Roo.isSafari){ // strip safari nonsense
21521                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21522             }
21523         }
21524         if(html == '&nbsp;'){
21525             html = '';
21526         }
21527         return html;
21528     },
21529
21530     /**
21531      * HTML Editor -> Textarea
21532      * Protected method that will not generally be called directly. Syncs the contents
21533      * of the editor iframe with the textarea.
21534      */
21535     syncValue : function(){
21536         if(this.initialized){
21537             var bd = (this.doc.body || this.doc.documentElement);
21538             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21539             var html = bd.innerHTML;
21540             if(Roo.isSafari){
21541                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21542                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21543                 if(m && m[1]){
21544                     html = '<div style="'+m[0]+'">' + html + '</div>';
21545                 }
21546             }
21547             html = this.cleanHtml(html);
21548             // fix up the special chars.. normaly like back quotes in word...
21549             // however we do not want to do this with chinese..
21550             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21551                 var cc = b.charCodeAt();
21552                 if (
21553                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21554                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21555                     (cc >= 0xf900 && cc < 0xfb00 )
21556                 ) {
21557                         return b;
21558                 }
21559                 return "&#"+cc+";" 
21560             });
21561             if(this.owner.fireEvent('beforesync', this, html) !== false){
21562                 this.el.dom.value = html;
21563                 this.owner.fireEvent('sync', this, html);
21564             }
21565         }
21566     },
21567
21568     /**
21569      * Protected method that will not generally be called directly. Pushes the value of the textarea
21570      * into the iframe editor.
21571      */
21572     pushValue : function(){
21573         if(this.initialized){
21574             var v = this.el.dom.value.trim();
21575             
21576 //            if(v.length < 1){
21577 //                v = '&#160;';
21578 //            }
21579             
21580             if(this.owner.fireEvent('beforepush', this, v) !== false){
21581                 var d = (this.doc.body || this.doc.documentElement);
21582                 d.innerHTML = v;
21583                 this.cleanUpPaste();
21584                 this.el.dom.value = d.innerHTML;
21585                 this.owner.fireEvent('push', this, v);
21586             }
21587         }
21588     },
21589
21590     // private
21591     deferFocus : function(){
21592         this.focus.defer(10, this);
21593     },
21594
21595     // doc'ed in Field
21596     focus : function(){
21597         if(this.win && !this.sourceEditMode){
21598             this.win.focus();
21599         }else{
21600             this.el.focus();
21601         }
21602     },
21603     
21604     assignDocWin: function()
21605     {
21606         var iframe = this.iframe;
21607         
21608          if(Roo.isIE){
21609             this.doc = iframe.contentWindow.document;
21610             this.win = iframe.contentWindow;
21611         } else {
21612 //            if (!Roo.get(this.frameId)) {
21613 //                return;
21614 //            }
21615 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21616 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21617             
21618             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21619                 return;
21620             }
21621             
21622             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21623             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21624         }
21625     },
21626     
21627     // private
21628     initEditor : function(){
21629         //console.log("INIT EDITOR");
21630         this.assignDocWin();
21631         
21632         
21633         
21634         this.doc.designMode="on";
21635         this.doc.open();
21636         this.doc.write(this.getDocMarkup());
21637         this.doc.close();
21638         
21639         var dbody = (this.doc.body || this.doc.documentElement);
21640         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21641         // this copies styles from the containing element into thsi one..
21642         // not sure why we need all of this..
21643         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21644         
21645         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21646         //ss['background-attachment'] = 'fixed'; // w3c
21647         dbody.bgProperties = 'fixed'; // ie
21648         //Roo.DomHelper.applyStyles(dbody, ss);
21649         Roo.EventManager.on(this.doc, {
21650             //'mousedown': this.onEditorEvent,
21651             'mouseup': this.onEditorEvent,
21652             'dblclick': this.onEditorEvent,
21653             'click': this.onEditorEvent,
21654             'keyup': this.onEditorEvent,
21655             buffer:100,
21656             scope: this
21657         });
21658         if(Roo.isGecko){
21659             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21660         }
21661         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21662             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21663         }
21664         this.initialized = true;
21665
21666         this.owner.fireEvent('initialize', this);
21667         this.pushValue();
21668     },
21669
21670     // private
21671     onDestroy : function(){
21672         
21673         
21674         
21675         if(this.rendered){
21676             
21677             //for (var i =0; i < this.toolbars.length;i++) {
21678             //    // fixme - ask toolbars for heights?
21679             //    this.toolbars[i].onDestroy();
21680            // }
21681             
21682             //this.wrap.dom.innerHTML = '';
21683             //this.wrap.remove();
21684         }
21685     },
21686
21687     // private
21688     onFirstFocus : function(){
21689         
21690         this.assignDocWin();
21691         
21692         
21693         this.activated = true;
21694          
21695     
21696         if(Roo.isGecko){ // prevent silly gecko errors
21697             this.win.focus();
21698             var s = this.win.getSelection();
21699             if(!s.focusNode || s.focusNode.nodeType != 3){
21700                 var r = s.getRangeAt(0);
21701                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21702                 r.collapse(true);
21703                 this.deferFocus();
21704             }
21705             try{
21706                 this.execCmd('useCSS', true);
21707                 this.execCmd('styleWithCSS', false);
21708             }catch(e){}
21709         }
21710         this.owner.fireEvent('activate', this);
21711     },
21712
21713     // private
21714     adjustFont: function(btn){
21715         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21716         //if(Roo.isSafari){ // safari
21717         //    adjust *= 2;
21718        // }
21719         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21720         if(Roo.isSafari){ // safari
21721             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21722             v =  (v < 10) ? 10 : v;
21723             v =  (v > 48) ? 48 : v;
21724             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21725             
21726         }
21727         
21728         
21729         v = Math.max(1, v+adjust);
21730         
21731         this.execCmd('FontSize', v  );
21732     },
21733
21734     onEditorEvent : function(e)
21735     {
21736         this.owner.fireEvent('editorevent', this, e);
21737       //  this.updateToolbar();
21738         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21739     },
21740
21741     insertTag : function(tg)
21742     {
21743         // could be a bit smarter... -> wrap the current selected tRoo..
21744         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21745             
21746             range = this.createRange(this.getSelection());
21747             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21748             wrappingNode.appendChild(range.extractContents());
21749             range.insertNode(wrappingNode);
21750
21751             return;
21752             
21753             
21754             
21755         }
21756         this.execCmd("formatblock",   tg);
21757         
21758     },
21759     
21760     insertText : function(txt)
21761     {
21762         
21763         
21764         var range = this.createRange();
21765         range.deleteContents();
21766                //alert(Sender.getAttribute('label'));
21767                
21768         range.insertNode(this.doc.createTextNode(txt));
21769     } ,
21770     
21771      
21772
21773     /**
21774      * Executes a Midas editor command on the editor document and performs necessary focus and
21775      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21776      * @param {String} cmd The Midas command
21777      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21778      */
21779     relayCmd : function(cmd, value){
21780         this.win.focus();
21781         this.execCmd(cmd, value);
21782         this.owner.fireEvent('editorevent', this);
21783         //this.updateToolbar();
21784         this.owner.deferFocus();
21785     },
21786
21787     /**
21788      * Executes a Midas editor command directly on the editor document.
21789      * For visual commands, you should use {@link #relayCmd} instead.
21790      * <b>This should only be called after the editor is initialized.</b>
21791      * @param {String} cmd The Midas command
21792      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21793      */
21794     execCmd : function(cmd, value){
21795         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21796         this.syncValue();
21797     },
21798  
21799  
21800    
21801     /**
21802      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21803      * to insert tRoo.
21804      * @param {String} text | dom node.. 
21805      */
21806     insertAtCursor : function(text)
21807     {
21808         
21809         if(!this.activated){
21810             return;
21811         }
21812         /*
21813         if(Roo.isIE){
21814             this.win.focus();
21815             var r = this.doc.selection.createRange();
21816             if(r){
21817                 r.collapse(true);
21818                 r.pasteHTML(text);
21819                 this.syncValue();
21820                 this.deferFocus();
21821             
21822             }
21823             return;
21824         }
21825         */
21826         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21827             this.win.focus();
21828             
21829             
21830             // from jquery ui (MIT licenced)
21831             var range, node;
21832             var win = this.win;
21833             
21834             if (win.getSelection && win.getSelection().getRangeAt) {
21835                 range = win.getSelection().getRangeAt(0);
21836                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21837                 range.insertNode(node);
21838             } else if (win.document.selection && win.document.selection.createRange) {
21839                 // no firefox support
21840                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21841                 win.document.selection.createRange().pasteHTML(txt);
21842             } else {
21843                 // no firefox support
21844                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21845                 this.execCmd('InsertHTML', txt);
21846             } 
21847             
21848             this.syncValue();
21849             
21850             this.deferFocus();
21851         }
21852     },
21853  // private
21854     mozKeyPress : function(e){
21855         if(e.ctrlKey){
21856             var c = e.getCharCode(), cmd;
21857           
21858             if(c > 0){
21859                 c = String.fromCharCode(c).toLowerCase();
21860                 switch(c){
21861                     case 'b':
21862                         cmd = 'bold';
21863                         break;
21864                     case 'i':
21865                         cmd = 'italic';
21866                         break;
21867                     
21868                     case 'u':
21869                         cmd = 'underline';
21870                         break;
21871                     
21872                     case 'v':
21873                         this.cleanUpPaste.defer(100, this);
21874                         return;
21875                         
21876                 }
21877                 if(cmd){
21878                     this.win.focus();
21879                     this.execCmd(cmd);
21880                     this.deferFocus();
21881                     e.preventDefault();
21882                 }
21883                 
21884             }
21885         }
21886     },
21887
21888     // private
21889     fixKeys : function(){ // load time branching for fastest keydown performance
21890         if(Roo.isIE){
21891             return function(e){
21892                 var k = e.getKey(), r;
21893                 if(k == e.TAB){
21894                     e.stopEvent();
21895                     r = this.doc.selection.createRange();
21896                     if(r){
21897                         r.collapse(true);
21898                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21899                         this.deferFocus();
21900                     }
21901                     return;
21902                 }
21903                 
21904                 if(k == e.ENTER){
21905                     r = this.doc.selection.createRange();
21906                     if(r){
21907                         var target = r.parentElement();
21908                         if(!target || target.tagName.toLowerCase() != 'li'){
21909                             e.stopEvent();
21910                             r.pasteHTML('<br />');
21911                             r.collapse(false);
21912                             r.select();
21913                         }
21914                     }
21915                 }
21916                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21917                     this.cleanUpPaste.defer(100, this);
21918                     return;
21919                 }
21920                 
21921                 
21922             };
21923         }else if(Roo.isOpera){
21924             return function(e){
21925                 var k = e.getKey();
21926                 if(k == e.TAB){
21927                     e.stopEvent();
21928                     this.win.focus();
21929                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21930                     this.deferFocus();
21931                 }
21932                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21933                     this.cleanUpPaste.defer(100, this);
21934                     return;
21935                 }
21936                 
21937             };
21938         }else if(Roo.isSafari){
21939             return function(e){
21940                 var k = e.getKey();
21941                 
21942                 if(k == e.TAB){
21943                     e.stopEvent();
21944                     this.execCmd('InsertText','\t');
21945                     this.deferFocus();
21946                     return;
21947                 }
21948                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21949                     this.cleanUpPaste.defer(100, this);
21950                     return;
21951                 }
21952                 
21953              };
21954         }
21955     }(),
21956     
21957     getAllAncestors: function()
21958     {
21959         var p = this.getSelectedNode();
21960         var a = [];
21961         if (!p) {
21962             a.push(p); // push blank onto stack..
21963             p = this.getParentElement();
21964         }
21965         
21966         
21967         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21968             a.push(p);
21969             p = p.parentNode;
21970         }
21971         a.push(this.doc.body);
21972         return a;
21973     },
21974     lastSel : false,
21975     lastSelNode : false,
21976     
21977     
21978     getSelection : function() 
21979     {
21980         this.assignDocWin();
21981         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21982     },
21983     
21984     getSelectedNode: function() 
21985     {
21986         // this may only work on Gecko!!!
21987         
21988         // should we cache this!!!!
21989         
21990         
21991         
21992          
21993         var range = this.createRange(this.getSelection()).cloneRange();
21994         
21995         if (Roo.isIE) {
21996             var parent = range.parentElement();
21997             while (true) {
21998                 var testRange = range.duplicate();
21999                 testRange.moveToElementText(parent);
22000                 if (testRange.inRange(range)) {
22001                     break;
22002                 }
22003                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22004                     break;
22005                 }
22006                 parent = parent.parentElement;
22007             }
22008             return parent;
22009         }
22010         
22011         // is ancestor a text element.
22012         var ac =  range.commonAncestorContainer;
22013         if (ac.nodeType == 3) {
22014             ac = ac.parentNode;
22015         }
22016         
22017         var ar = ac.childNodes;
22018          
22019         var nodes = [];
22020         var other_nodes = [];
22021         var has_other_nodes = false;
22022         for (var i=0;i<ar.length;i++) {
22023             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22024                 continue;
22025             }
22026             // fullly contained node.
22027             
22028             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22029                 nodes.push(ar[i]);
22030                 continue;
22031             }
22032             
22033             // probably selected..
22034             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22035                 other_nodes.push(ar[i]);
22036                 continue;
22037             }
22038             // outer..
22039             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22040                 continue;
22041             }
22042             
22043             
22044             has_other_nodes = true;
22045         }
22046         if (!nodes.length && other_nodes.length) {
22047             nodes= other_nodes;
22048         }
22049         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22050             return false;
22051         }
22052         
22053         return nodes[0];
22054     },
22055     createRange: function(sel)
22056     {
22057         // this has strange effects when using with 
22058         // top toolbar - not sure if it's a great idea.
22059         //this.editor.contentWindow.focus();
22060         if (typeof sel != "undefined") {
22061             try {
22062                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22063             } catch(e) {
22064                 return this.doc.createRange();
22065             }
22066         } else {
22067             return this.doc.createRange();
22068         }
22069     },
22070     getParentElement: function()
22071     {
22072         
22073         this.assignDocWin();
22074         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22075         
22076         var range = this.createRange(sel);
22077          
22078         try {
22079             var p = range.commonAncestorContainer;
22080             while (p.nodeType == 3) { // text node
22081                 p = p.parentNode;
22082             }
22083             return p;
22084         } catch (e) {
22085             return null;
22086         }
22087     
22088     },
22089     /***
22090      *
22091      * Range intersection.. the hard stuff...
22092      *  '-1' = before
22093      *  '0' = hits..
22094      *  '1' = after.
22095      *         [ -- selected range --- ]
22096      *   [fail]                        [fail]
22097      *
22098      *    basically..
22099      *      if end is before start or  hits it. fail.
22100      *      if start is after end or hits it fail.
22101      *
22102      *   if either hits (but other is outside. - then it's not 
22103      *   
22104      *    
22105      **/
22106     
22107     
22108     // @see http://www.thismuchiknow.co.uk/?p=64.
22109     rangeIntersectsNode : function(range, node)
22110     {
22111         var nodeRange = node.ownerDocument.createRange();
22112         try {
22113             nodeRange.selectNode(node);
22114         } catch (e) {
22115             nodeRange.selectNodeContents(node);
22116         }
22117     
22118         var rangeStartRange = range.cloneRange();
22119         rangeStartRange.collapse(true);
22120     
22121         var rangeEndRange = range.cloneRange();
22122         rangeEndRange.collapse(false);
22123     
22124         var nodeStartRange = nodeRange.cloneRange();
22125         nodeStartRange.collapse(true);
22126     
22127         var nodeEndRange = nodeRange.cloneRange();
22128         nodeEndRange.collapse(false);
22129     
22130         return rangeStartRange.compareBoundaryPoints(
22131                  Range.START_TO_START, nodeEndRange) == -1 &&
22132                rangeEndRange.compareBoundaryPoints(
22133                  Range.START_TO_START, nodeStartRange) == 1;
22134         
22135          
22136     },
22137     rangeCompareNode : function(range, node)
22138     {
22139         var nodeRange = node.ownerDocument.createRange();
22140         try {
22141             nodeRange.selectNode(node);
22142         } catch (e) {
22143             nodeRange.selectNodeContents(node);
22144         }
22145         
22146         
22147         range.collapse(true);
22148     
22149         nodeRange.collapse(true);
22150      
22151         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22152         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22153          
22154         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22155         
22156         var nodeIsBefore   =  ss == 1;
22157         var nodeIsAfter    = ee == -1;
22158         
22159         if (nodeIsBefore && nodeIsAfter) {
22160             return 0; // outer
22161         }
22162         if (!nodeIsBefore && nodeIsAfter) {
22163             return 1; //right trailed.
22164         }
22165         
22166         if (nodeIsBefore && !nodeIsAfter) {
22167             return 2;  // left trailed.
22168         }
22169         // fully contined.
22170         return 3;
22171     },
22172
22173     // private? - in a new class?
22174     cleanUpPaste :  function()
22175     {
22176         // cleans up the whole document..
22177         Roo.log('cleanuppaste');
22178         
22179         this.cleanUpChildren(this.doc.body);
22180         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22181         if (clean != this.doc.body.innerHTML) {
22182             this.doc.body.innerHTML = clean;
22183         }
22184         
22185     },
22186     
22187     cleanWordChars : function(input) {// change the chars to hex code
22188         var he = Roo.HtmlEditorCore;
22189         
22190         var output = input;
22191         Roo.each(he.swapCodes, function(sw) { 
22192             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22193             
22194             output = output.replace(swapper, sw[1]);
22195         });
22196         
22197         return output;
22198     },
22199     
22200     
22201     cleanUpChildren : function (n)
22202     {
22203         if (!n.childNodes.length) {
22204             return;
22205         }
22206         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22207            this.cleanUpChild(n.childNodes[i]);
22208         }
22209     },
22210     
22211     
22212         
22213     
22214     cleanUpChild : function (node)
22215     {
22216         var ed = this;
22217         //console.log(node);
22218         if (node.nodeName == "#text") {
22219             // clean up silly Windows -- stuff?
22220             return; 
22221         }
22222         if (node.nodeName == "#comment") {
22223             node.parentNode.removeChild(node);
22224             // clean up silly Windows -- stuff?
22225             return; 
22226         }
22227         var lcname = node.tagName.toLowerCase();
22228         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22229         // whitelist of tags..
22230         
22231         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22232             // remove node.
22233             node.parentNode.removeChild(node);
22234             return;
22235             
22236         }
22237         
22238         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22239         
22240         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22241         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22242         
22243         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22244         //    remove_keep_children = true;
22245         //}
22246         
22247         if (remove_keep_children) {
22248             this.cleanUpChildren(node);
22249             // inserts everything just before this node...
22250             while (node.childNodes.length) {
22251                 var cn = node.childNodes[0];
22252                 node.removeChild(cn);
22253                 node.parentNode.insertBefore(cn, node);
22254             }
22255             node.parentNode.removeChild(node);
22256             return;
22257         }
22258         
22259         if (!node.attributes || !node.attributes.length) {
22260             this.cleanUpChildren(node);
22261             return;
22262         }
22263         
22264         function cleanAttr(n,v)
22265         {
22266             
22267             if (v.match(/^\./) || v.match(/^\//)) {
22268                 return;
22269             }
22270             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22271                 return;
22272             }
22273             if (v.match(/^#/)) {
22274                 return;
22275             }
22276 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22277             node.removeAttribute(n);
22278             
22279         }
22280         
22281         var cwhite = this.cwhite;
22282         var cblack = this.cblack;
22283             
22284         function cleanStyle(n,v)
22285         {
22286             if (v.match(/expression/)) { //XSS?? should we even bother..
22287                 node.removeAttribute(n);
22288                 return;
22289             }
22290             
22291             var parts = v.split(/;/);
22292             var clean = [];
22293             
22294             Roo.each(parts, function(p) {
22295                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22296                 if (!p.length) {
22297                     return true;
22298                 }
22299                 var l = p.split(':').shift().replace(/\s+/g,'');
22300                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22301                 
22302                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22303 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22304                     //node.removeAttribute(n);
22305                     return true;
22306                 }
22307                 //Roo.log()
22308                 // only allow 'c whitelisted system attributes'
22309                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22310 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22311                     //node.removeAttribute(n);
22312                     return true;
22313                 }
22314                 
22315                 
22316                  
22317                 
22318                 clean.push(p);
22319                 return true;
22320             });
22321             if (clean.length) { 
22322                 node.setAttribute(n, clean.join(';'));
22323             } else {
22324                 node.removeAttribute(n);
22325             }
22326             
22327         }
22328         
22329         
22330         for (var i = node.attributes.length-1; i > -1 ; i--) {
22331             var a = node.attributes[i];
22332             //console.log(a);
22333             
22334             if (a.name.toLowerCase().substr(0,2)=='on')  {
22335                 node.removeAttribute(a.name);
22336                 continue;
22337             }
22338             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22339                 node.removeAttribute(a.name);
22340                 continue;
22341             }
22342             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22343                 cleanAttr(a.name,a.value); // fixme..
22344                 continue;
22345             }
22346             if (a.name == 'style') {
22347                 cleanStyle(a.name,a.value);
22348                 continue;
22349             }
22350             /// clean up MS crap..
22351             // tecnically this should be a list of valid class'es..
22352             
22353             
22354             if (a.name == 'class') {
22355                 if (a.value.match(/^Mso/)) {
22356                     node.className = '';
22357                 }
22358                 
22359                 if (a.value.match(/^body$/)) {
22360                     node.className = '';
22361                 }
22362                 continue;
22363             }
22364             
22365             // style cleanup!?
22366             // class cleanup?
22367             
22368         }
22369         
22370         
22371         this.cleanUpChildren(node);
22372         
22373         
22374     },
22375     
22376     /**
22377      * Clean up MS wordisms...
22378      */
22379     cleanWord : function(node)
22380     {
22381         
22382         
22383         if (!node) {
22384             this.cleanWord(this.doc.body);
22385             return;
22386         }
22387         if (node.nodeName == "#text") {
22388             // clean up silly Windows -- stuff?
22389             return; 
22390         }
22391         if (node.nodeName == "#comment") {
22392             node.parentNode.removeChild(node);
22393             // clean up silly Windows -- stuff?
22394             return; 
22395         }
22396         
22397         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22398             node.parentNode.removeChild(node);
22399             return;
22400         }
22401         
22402         // remove - but keep children..
22403         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22404             while (node.childNodes.length) {
22405                 var cn = node.childNodes[0];
22406                 node.removeChild(cn);
22407                 node.parentNode.insertBefore(cn, node);
22408             }
22409             node.parentNode.removeChild(node);
22410             this.iterateChildren(node, this.cleanWord);
22411             return;
22412         }
22413         // clean styles
22414         if (node.className.length) {
22415             
22416             var cn = node.className.split(/\W+/);
22417             var cna = [];
22418             Roo.each(cn, function(cls) {
22419                 if (cls.match(/Mso[a-zA-Z]+/)) {
22420                     return;
22421                 }
22422                 cna.push(cls);
22423             });
22424             node.className = cna.length ? cna.join(' ') : '';
22425             if (!cna.length) {
22426                 node.removeAttribute("class");
22427             }
22428         }
22429         
22430         if (node.hasAttribute("lang")) {
22431             node.removeAttribute("lang");
22432         }
22433         
22434         if (node.hasAttribute("style")) {
22435             
22436             var styles = node.getAttribute("style").split(";");
22437             var nstyle = [];
22438             Roo.each(styles, function(s) {
22439                 if (!s.match(/:/)) {
22440                     return;
22441                 }
22442                 var kv = s.split(":");
22443                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22444                     return;
22445                 }
22446                 // what ever is left... we allow.
22447                 nstyle.push(s);
22448             });
22449             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22450             if (!nstyle.length) {
22451                 node.removeAttribute('style');
22452             }
22453         }
22454         this.iterateChildren(node, this.cleanWord);
22455         
22456         
22457         
22458     },
22459     /**
22460      * iterateChildren of a Node, calling fn each time, using this as the scole..
22461      * @param {DomNode} node node to iterate children of.
22462      * @param {Function} fn method of this class to call on each item.
22463      */
22464     iterateChildren : function(node, fn)
22465     {
22466         if (!node.childNodes.length) {
22467                 return;
22468         }
22469         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22470            fn.call(this, node.childNodes[i])
22471         }
22472     },
22473     
22474     
22475     /**
22476      * cleanTableWidths.
22477      *
22478      * Quite often pasting from word etc.. results in tables with column and widths.
22479      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22480      *
22481      */
22482     cleanTableWidths : function(node)
22483     {
22484          
22485          
22486         if (!node) {
22487             this.cleanTableWidths(this.doc.body);
22488             return;
22489         }
22490         
22491         // ignore list...
22492         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22493             return; 
22494         }
22495         Roo.log(node.tagName);
22496         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22497             this.iterateChildren(node, this.cleanTableWidths);
22498             return;
22499         }
22500         if (node.hasAttribute('width')) {
22501             node.removeAttribute('width');
22502         }
22503         
22504          
22505         if (node.hasAttribute("style")) {
22506             // pretty basic...
22507             
22508             var styles = node.getAttribute("style").split(";");
22509             var nstyle = [];
22510             Roo.each(styles, function(s) {
22511                 if (!s.match(/:/)) {
22512                     return;
22513                 }
22514                 var kv = s.split(":");
22515                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22516                     return;
22517                 }
22518                 // what ever is left... we allow.
22519                 nstyle.push(s);
22520             });
22521             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22522             if (!nstyle.length) {
22523                 node.removeAttribute('style');
22524             }
22525         }
22526         
22527         this.iterateChildren(node, this.cleanTableWidths);
22528         
22529         
22530     },
22531     
22532     
22533     
22534     
22535     domToHTML : function(currentElement, depth, nopadtext) {
22536         
22537         depth = depth || 0;
22538         nopadtext = nopadtext || false;
22539     
22540         if (!currentElement) {
22541             return this.domToHTML(this.doc.body);
22542         }
22543         
22544         //Roo.log(currentElement);
22545         var j;
22546         var allText = false;
22547         var nodeName = currentElement.nodeName;
22548         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22549         
22550         if  (nodeName == '#text') {
22551             
22552             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22553         }
22554         
22555         
22556         var ret = '';
22557         if (nodeName != 'BODY') {
22558              
22559             var i = 0;
22560             // Prints the node tagName, such as <A>, <IMG>, etc
22561             if (tagName) {
22562                 var attr = [];
22563                 for(i = 0; i < currentElement.attributes.length;i++) {
22564                     // quoting?
22565                     var aname = currentElement.attributes.item(i).name;
22566                     if (!currentElement.attributes.item(i).value.length) {
22567                         continue;
22568                     }
22569                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22570                 }
22571                 
22572                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22573             } 
22574             else {
22575                 
22576                 // eack
22577             }
22578         } else {
22579             tagName = false;
22580         }
22581         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22582             return ret;
22583         }
22584         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22585             nopadtext = true;
22586         }
22587         
22588         
22589         // Traverse the tree
22590         i = 0;
22591         var currentElementChild = currentElement.childNodes.item(i);
22592         var allText = true;
22593         var innerHTML  = '';
22594         lastnode = '';
22595         while (currentElementChild) {
22596             // Formatting code (indent the tree so it looks nice on the screen)
22597             var nopad = nopadtext;
22598             if (lastnode == 'SPAN') {
22599                 nopad  = true;
22600             }
22601             // text
22602             if  (currentElementChild.nodeName == '#text') {
22603                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22604                 toadd = nopadtext ? toadd : toadd.trim();
22605                 if (!nopad && toadd.length > 80) {
22606                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22607                 }
22608                 innerHTML  += toadd;
22609                 
22610                 i++;
22611                 currentElementChild = currentElement.childNodes.item(i);
22612                 lastNode = '';
22613                 continue;
22614             }
22615             allText = false;
22616             
22617             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22618                 
22619             // Recursively traverse the tree structure of the child node
22620             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22621             lastnode = currentElementChild.nodeName;
22622             i++;
22623             currentElementChild=currentElement.childNodes.item(i);
22624         }
22625         
22626         ret += innerHTML;
22627         
22628         if (!allText) {
22629                 // The remaining code is mostly for formatting the tree
22630             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22631         }
22632         
22633         
22634         if (tagName) {
22635             ret+= "</"+tagName+">";
22636         }
22637         return ret;
22638         
22639     },
22640         
22641     applyBlacklists : function()
22642     {
22643         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22644         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22645         
22646         this.white = [];
22647         this.black = [];
22648         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22649             if (b.indexOf(tag) > -1) {
22650                 return;
22651             }
22652             this.white.push(tag);
22653             
22654         }, this);
22655         
22656         Roo.each(w, function(tag) {
22657             if (b.indexOf(tag) > -1) {
22658                 return;
22659             }
22660             if (this.white.indexOf(tag) > -1) {
22661                 return;
22662             }
22663             this.white.push(tag);
22664             
22665         }, this);
22666         
22667         
22668         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22669             if (w.indexOf(tag) > -1) {
22670                 return;
22671             }
22672             this.black.push(tag);
22673             
22674         }, this);
22675         
22676         Roo.each(b, function(tag) {
22677             if (w.indexOf(tag) > -1) {
22678                 return;
22679             }
22680             if (this.black.indexOf(tag) > -1) {
22681                 return;
22682             }
22683             this.black.push(tag);
22684             
22685         }, this);
22686         
22687         
22688         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22689         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22690         
22691         this.cwhite = [];
22692         this.cblack = [];
22693         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22694             if (b.indexOf(tag) > -1) {
22695                 return;
22696             }
22697             this.cwhite.push(tag);
22698             
22699         }, this);
22700         
22701         Roo.each(w, function(tag) {
22702             if (b.indexOf(tag) > -1) {
22703                 return;
22704             }
22705             if (this.cwhite.indexOf(tag) > -1) {
22706                 return;
22707             }
22708             this.cwhite.push(tag);
22709             
22710         }, this);
22711         
22712         
22713         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22714             if (w.indexOf(tag) > -1) {
22715                 return;
22716             }
22717             this.cblack.push(tag);
22718             
22719         }, this);
22720         
22721         Roo.each(b, function(tag) {
22722             if (w.indexOf(tag) > -1) {
22723                 return;
22724             }
22725             if (this.cblack.indexOf(tag) > -1) {
22726                 return;
22727             }
22728             this.cblack.push(tag);
22729             
22730         }, this);
22731     },
22732     
22733     setStylesheets : function(stylesheets)
22734     {
22735         if(typeof(stylesheets) == 'string'){
22736             Roo.get(this.iframe.contentDocument.head).createChild({
22737                 tag : 'link',
22738                 rel : 'stylesheet',
22739                 type : 'text/css',
22740                 href : stylesheets
22741             });
22742             
22743             return;
22744         }
22745         var _this = this;
22746      
22747         Roo.each(stylesheets, function(s) {
22748             if(!s.length){
22749                 return;
22750             }
22751             
22752             Roo.get(_this.iframe.contentDocument.head).createChild({
22753                 tag : 'link',
22754                 rel : 'stylesheet',
22755                 type : 'text/css',
22756                 href : s
22757             });
22758         });
22759
22760         
22761     },
22762     
22763     removeStylesheets : function()
22764     {
22765         var _this = this;
22766         
22767         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22768             s.remove();
22769         });
22770     },
22771     
22772     setStyle : function(style)
22773     {
22774         Roo.get(this.iframe.contentDocument.head).createChild({
22775             tag : 'style',
22776             type : 'text/css',
22777             html : style
22778         });
22779
22780         return;
22781     }
22782     
22783     // hide stuff that is not compatible
22784     /**
22785      * @event blur
22786      * @hide
22787      */
22788     /**
22789      * @event change
22790      * @hide
22791      */
22792     /**
22793      * @event focus
22794      * @hide
22795      */
22796     /**
22797      * @event specialkey
22798      * @hide
22799      */
22800     /**
22801      * @cfg {String} fieldClass @hide
22802      */
22803     /**
22804      * @cfg {String} focusClass @hide
22805      */
22806     /**
22807      * @cfg {String} autoCreate @hide
22808      */
22809     /**
22810      * @cfg {String} inputType @hide
22811      */
22812     /**
22813      * @cfg {String} invalidClass @hide
22814      */
22815     /**
22816      * @cfg {String} invalidText @hide
22817      */
22818     /**
22819      * @cfg {String} msgFx @hide
22820      */
22821     /**
22822      * @cfg {String} validateOnBlur @hide
22823      */
22824 });
22825
22826 Roo.HtmlEditorCore.white = [
22827         'area', 'br', 'img', 'input', 'hr', 'wbr',
22828         
22829        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22830        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22831        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22832        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22833        'table',   'ul',         'xmp', 
22834        
22835        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22836       'thead',   'tr', 
22837      
22838       'dir', 'menu', 'ol', 'ul', 'dl',
22839        
22840       'embed',  'object'
22841 ];
22842
22843
22844 Roo.HtmlEditorCore.black = [
22845     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22846         'applet', // 
22847         'base',   'basefont', 'bgsound', 'blink',  'body', 
22848         'frame',  'frameset', 'head',    'html',   'ilayer', 
22849         'iframe', 'layer',  'link',     'meta',    'object',   
22850         'script', 'style' ,'title',  'xml' // clean later..
22851 ];
22852 Roo.HtmlEditorCore.clean = [
22853     'script', 'style', 'title', 'xml'
22854 ];
22855 Roo.HtmlEditorCore.remove = [
22856     'font'
22857 ];
22858 // attributes..
22859
22860 Roo.HtmlEditorCore.ablack = [
22861     'on'
22862 ];
22863     
22864 Roo.HtmlEditorCore.aclean = [ 
22865     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22866 ];
22867
22868 // protocols..
22869 Roo.HtmlEditorCore.pwhite= [
22870         'http',  'https',  'mailto'
22871 ];
22872
22873 // white listed style attributes.
22874 Roo.HtmlEditorCore.cwhite= [
22875       //  'text-align', /// default is to allow most things..
22876       
22877          
22878 //        'font-size'//??
22879 ];
22880
22881 // black listed style attributes.
22882 Roo.HtmlEditorCore.cblack= [
22883       //  'font-size' -- this can be set by the project 
22884 ];
22885
22886
22887 Roo.HtmlEditorCore.swapCodes   =[ 
22888     [    8211, "--" ], 
22889     [    8212, "--" ], 
22890     [    8216,  "'" ],  
22891     [    8217, "'" ],  
22892     [    8220, '"' ],  
22893     [    8221, '"' ],  
22894     [    8226, "*" ],  
22895     [    8230, "..." ]
22896 ]; 
22897
22898     /*
22899  * - LGPL
22900  *
22901  * HtmlEditor
22902  * 
22903  */
22904
22905 /**
22906  * @class Roo.bootstrap.HtmlEditor
22907  * @extends Roo.bootstrap.TextArea
22908  * Bootstrap HtmlEditor class
22909
22910  * @constructor
22911  * Create a new HtmlEditor
22912  * @param {Object} config The config object
22913  */
22914
22915 Roo.bootstrap.HtmlEditor = function(config){
22916     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22917     if (!this.toolbars) {
22918         this.toolbars = [];
22919     }
22920     
22921     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22922     this.addEvents({
22923             /**
22924              * @event initialize
22925              * Fires when the editor is fully initialized (including the iframe)
22926              * @param {HtmlEditor} this
22927              */
22928             initialize: true,
22929             /**
22930              * @event activate
22931              * Fires when the editor is first receives the focus. Any insertion must wait
22932              * until after this event.
22933              * @param {HtmlEditor} this
22934              */
22935             activate: true,
22936              /**
22937              * @event beforesync
22938              * Fires before the textarea is updated with content from the editor iframe. Return false
22939              * to cancel the sync.
22940              * @param {HtmlEditor} this
22941              * @param {String} html
22942              */
22943             beforesync: true,
22944              /**
22945              * @event beforepush
22946              * Fires before the iframe editor is updated with content from the textarea. Return false
22947              * to cancel the push.
22948              * @param {HtmlEditor} this
22949              * @param {String} html
22950              */
22951             beforepush: true,
22952              /**
22953              * @event sync
22954              * Fires when the textarea is updated with content from the editor iframe.
22955              * @param {HtmlEditor} this
22956              * @param {String} html
22957              */
22958             sync: true,
22959              /**
22960              * @event push
22961              * Fires when the iframe editor is updated with content from the textarea.
22962              * @param {HtmlEditor} this
22963              * @param {String} html
22964              */
22965             push: true,
22966              /**
22967              * @event editmodechange
22968              * Fires when the editor switches edit modes
22969              * @param {HtmlEditor} this
22970              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22971              */
22972             editmodechange: true,
22973             /**
22974              * @event editorevent
22975              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22976              * @param {HtmlEditor} this
22977              */
22978             editorevent: true,
22979             /**
22980              * @event firstfocus
22981              * Fires when on first focus - needed by toolbars..
22982              * @param {HtmlEditor} this
22983              */
22984             firstfocus: true,
22985             /**
22986              * @event autosave
22987              * Auto save the htmlEditor value as a file into Events
22988              * @param {HtmlEditor} this
22989              */
22990             autosave: true,
22991             /**
22992              * @event savedpreview
22993              * preview the saved version of htmlEditor
22994              * @param {HtmlEditor} this
22995              */
22996             savedpreview: true
22997         });
22998 };
22999
23000
23001 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23002     
23003     
23004       /**
23005      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23006      */
23007     toolbars : false,
23008     
23009      /**
23010     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23011     */
23012     btns : [],
23013    
23014      /**
23015      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23016      *                        Roo.resizable.
23017      */
23018     resizable : false,
23019      /**
23020      * @cfg {Number} height (in pixels)
23021      */   
23022     height: 300,
23023    /**
23024      * @cfg {Number} width (in pixels)
23025      */   
23026     width: false,
23027     
23028     /**
23029      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23030      * 
23031      */
23032     stylesheets: false,
23033     
23034     // id of frame..
23035     frameId: false,
23036     
23037     // private properties
23038     validationEvent : false,
23039     deferHeight: true,
23040     initialized : false,
23041     activated : false,
23042     
23043     onFocus : Roo.emptyFn,
23044     iframePad:3,
23045     hideMode:'offsets',
23046     
23047     tbContainer : false,
23048     
23049     bodyCls : '',
23050     
23051     toolbarContainer :function() {
23052         return this.wrap.select('.x-html-editor-tb',true).first();
23053     },
23054
23055     /**
23056      * Protected method that will not generally be called directly. It
23057      * is called when the editor creates its toolbar. Override this method if you need to
23058      * add custom toolbar buttons.
23059      * @param {HtmlEditor} editor
23060      */
23061     createToolbar : function(){
23062         Roo.log('renewing');
23063         Roo.log("create toolbars");
23064         
23065         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23066         this.toolbars[0].render(this.toolbarContainer());
23067         
23068         return;
23069         
23070 //        if (!editor.toolbars || !editor.toolbars.length) {
23071 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23072 //        }
23073 //        
23074 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23075 //            editor.toolbars[i] = Roo.factory(
23076 //                    typeof(editor.toolbars[i]) == 'string' ?
23077 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23078 //                Roo.bootstrap.HtmlEditor);
23079 //            editor.toolbars[i].init(editor);
23080 //        }
23081     },
23082
23083      
23084     // private
23085     onRender : function(ct, position)
23086     {
23087        // Roo.log("Call onRender: " + this.xtype);
23088         var _t = this;
23089         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23090       
23091         this.wrap = this.inputEl().wrap({
23092             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23093         });
23094         
23095         this.editorcore.onRender(ct, position);
23096          
23097         if (this.resizable) {
23098             this.resizeEl = new Roo.Resizable(this.wrap, {
23099                 pinned : true,
23100                 wrap: true,
23101                 dynamic : true,
23102                 minHeight : this.height,
23103                 height: this.height,
23104                 handles : this.resizable,
23105                 width: this.width,
23106                 listeners : {
23107                     resize : function(r, w, h) {
23108                         _t.onResize(w,h); // -something
23109                     }
23110                 }
23111             });
23112             
23113         }
23114         this.createToolbar(this);
23115        
23116         
23117         if(!this.width && this.resizable){
23118             this.setSize(this.wrap.getSize());
23119         }
23120         if (this.resizeEl) {
23121             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23122             // should trigger onReize..
23123         }
23124         
23125     },
23126
23127     // private
23128     onResize : function(w, h)
23129     {
23130         Roo.log('resize: ' +w + ',' + h );
23131         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23132         var ew = false;
23133         var eh = false;
23134         
23135         if(this.inputEl() ){
23136             if(typeof w == 'number'){
23137                 var aw = w - this.wrap.getFrameWidth('lr');
23138                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23139                 ew = aw;
23140             }
23141             if(typeof h == 'number'){
23142                  var tbh = -11;  // fixme it needs to tool bar size!
23143                 for (var i =0; i < this.toolbars.length;i++) {
23144                     // fixme - ask toolbars for heights?
23145                     tbh += this.toolbars[i].el.getHeight();
23146                     //if (this.toolbars[i].footer) {
23147                     //    tbh += this.toolbars[i].footer.el.getHeight();
23148                     //}
23149                 }
23150               
23151                 
23152                 
23153                 
23154                 
23155                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23156                 ah -= 5; // knock a few pixes off for look..
23157                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23158                 var eh = ah;
23159             }
23160         }
23161         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23162         this.editorcore.onResize(ew,eh);
23163         
23164     },
23165
23166     /**
23167      * Toggles the editor between standard and source edit mode.
23168      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23169      */
23170     toggleSourceEdit : function(sourceEditMode)
23171     {
23172         this.editorcore.toggleSourceEdit(sourceEditMode);
23173         
23174         if(this.editorcore.sourceEditMode){
23175             Roo.log('editor - showing textarea');
23176             
23177 //            Roo.log('in');
23178 //            Roo.log(this.syncValue());
23179             this.syncValue();
23180             this.inputEl().removeClass(['hide', 'x-hidden']);
23181             this.inputEl().dom.removeAttribute('tabIndex');
23182             this.inputEl().focus();
23183         }else{
23184             Roo.log('editor - hiding textarea');
23185 //            Roo.log('out')
23186 //            Roo.log(this.pushValue()); 
23187             this.pushValue();
23188             
23189             this.inputEl().addClass(['hide', 'x-hidden']);
23190             this.inputEl().dom.setAttribute('tabIndex', -1);
23191             //this.deferFocus();
23192         }
23193          
23194         if(this.resizable){
23195             this.setSize(this.wrap.getSize());
23196         }
23197         
23198         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23199     },
23200  
23201     // private (for BoxComponent)
23202     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23203
23204     // private (for BoxComponent)
23205     getResizeEl : function(){
23206         return this.wrap;
23207     },
23208
23209     // private (for BoxComponent)
23210     getPositionEl : function(){
23211         return this.wrap;
23212     },
23213
23214     // private
23215     initEvents : function(){
23216         this.originalValue = this.getValue();
23217     },
23218
23219 //    /**
23220 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23221 //     * @method
23222 //     */
23223 //    markInvalid : Roo.emptyFn,
23224 //    /**
23225 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23226 //     * @method
23227 //     */
23228 //    clearInvalid : Roo.emptyFn,
23229
23230     setValue : function(v){
23231         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23232         this.editorcore.pushValue();
23233     },
23234
23235      
23236     // private
23237     deferFocus : function(){
23238         this.focus.defer(10, this);
23239     },
23240
23241     // doc'ed in Field
23242     focus : function(){
23243         this.editorcore.focus();
23244         
23245     },
23246       
23247
23248     // private
23249     onDestroy : function(){
23250         
23251         
23252         
23253         if(this.rendered){
23254             
23255             for (var i =0; i < this.toolbars.length;i++) {
23256                 // fixme - ask toolbars for heights?
23257                 this.toolbars[i].onDestroy();
23258             }
23259             
23260             this.wrap.dom.innerHTML = '';
23261             this.wrap.remove();
23262         }
23263     },
23264
23265     // private
23266     onFirstFocus : function(){
23267         //Roo.log("onFirstFocus");
23268         this.editorcore.onFirstFocus();
23269          for (var i =0; i < this.toolbars.length;i++) {
23270             this.toolbars[i].onFirstFocus();
23271         }
23272         
23273     },
23274     
23275     // private
23276     syncValue : function()
23277     {   
23278         this.editorcore.syncValue();
23279     },
23280     
23281     pushValue : function()
23282     {   
23283         this.editorcore.pushValue();
23284     }
23285      
23286     
23287     // hide stuff that is not compatible
23288     /**
23289      * @event blur
23290      * @hide
23291      */
23292     /**
23293      * @event change
23294      * @hide
23295      */
23296     /**
23297      * @event focus
23298      * @hide
23299      */
23300     /**
23301      * @event specialkey
23302      * @hide
23303      */
23304     /**
23305      * @cfg {String} fieldClass @hide
23306      */
23307     /**
23308      * @cfg {String} focusClass @hide
23309      */
23310     /**
23311      * @cfg {String} autoCreate @hide
23312      */
23313     /**
23314      * @cfg {String} inputType @hide
23315      */
23316     /**
23317      * @cfg {String} invalidClass @hide
23318      */
23319     /**
23320      * @cfg {String} invalidText @hide
23321      */
23322     /**
23323      * @cfg {String} msgFx @hide
23324      */
23325     /**
23326      * @cfg {String} validateOnBlur @hide
23327      */
23328 });
23329  
23330     
23331    
23332    
23333    
23334       
23335 Roo.namespace('Roo.bootstrap.htmleditor');
23336 /**
23337  * @class Roo.bootstrap.HtmlEditorToolbar1
23338  * Basic Toolbar
23339  * 
23340  * Usage:
23341  *
23342  new Roo.bootstrap.HtmlEditor({
23343     ....
23344     toolbars : [
23345         new Roo.bootstrap.HtmlEditorToolbar1({
23346             disable : { fonts: 1 , format: 1, ..., ... , ...],
23347             btns : [ .... ]
23348         })
23349     }
23350      
23351  * 
23352  * @cfg {Object} disable List of elements to disable..
23353  * @cfg {Array} btns List of additional buttons.
23354  * 
23355  * 
23356  * NEEDS Extra CSS? 
23357  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23358  */
23359  
23360 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23361 {
23362     
23363     Roo.apply(this, config);
23364     
23365     // default disabled, based on 'good practice'..
23366     this.disable = this.disable || {};
23367     Roo.applyIf(this.disable, {
23368         fontSize : true,
23369         colors : true,
23370         specialElements : true
23371     });
23372     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23373     
23374     this.editor = config.editor;
23375     this.editorcore = config.editor.editorcore;
23376     
23377     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23378     
23379     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23380     // dont call parent... till later.
23381 }
23382 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23383      
23384     bar : true,
23385     
23386     editor : false,
23387     editorcore : false,
23388     
23389     
23390     formats : [
23391         "p" ,  
23392         "h1","h2","h3","h4","h5","h6", 
23393         "pre", "code", 
23394         "abbr", "acronym", "address", "cite", "samp", "var",
23395         'div','span'
23396     ],
23397     
23398     onRender : function(ct, position)
23399     {
23400        // Roo.log("Call onRender: " + this.xtype);
23401         
23402        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23403        Roo.log(this.el);
23404        this.el.dom.style.marginBottom = '0';
23405        var _this = this;
23406        var editorcore = this.editorcore;
23407        var editor= this.editor;
23408        
23409        var children = [];
23410        var btn = function(id,cmd , toggle, handler, html){
23411        
23412             var  event = toggle ? 'toggle' : 'click';
23413        
23414             var a = {
23415                 size : 'sm',
23416                 xtype: 'Button',
23417                 xns: Roo.bootstrap,
23418                 glyphicon : id,
23419                 cmd : id || cmd,
23420                 enableToggle:toggle !== false,
23421                 html : html || '',
23422                 pressed : toggle ? false : null,
23423                 listeners : {}
23424             };
23425             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23426                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23427             };
23428             children.push(a);
23429             return a;
23430        }
23431        
23432     //    var cb_box = function...
23433         
23434         var style = {
23435                 xtype: 'Button',
23436                 size : 'sm',
23437                 xns: Roo.bootstrap,
23438                 glyphicon : 'font',
23439                 //html : 'submit'
23440                 menu : {
23441                     xtype: 'Menu',
23442                     xns: Roo.bootstrap,
23443                     items:  []
23444                 }
23445         };
23446         Roo.each(this.formats, function(f) {
23447             style.menu.items.push({
23448                 xtype :'MenuItem',
23449                 xns: Roo.bootstrap,
23450                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23451                 tagname : f,
23452                 listeners : {
23453                     click : function()
23454                     {
23455                         editorcore.insertTag(this.tagname);
23456                         editor.focus();
23457                     }
23458                 }
23459                 
23460             });
23461         });
23462         children.push(style);   
23463         
23464         btn('bold',false,true);
23465         btn('italic',false,true);
23466         btn('align-left', 'justifyleft',true);
23467         btn('align-center', 'justifycenter',true);
23468         btn('align-right' , 'justifyright',true);
23469         btn('link', false, false, function(btn) {
23470             //Roo.log("create link?");
23471             var url = prompt(this.createLinkText, this.defaultLinkValue);
23472             if(url && url != 'http:/'+'/'){
23473                 this.editorcore.relayCmd('createlink', url);
23474             }
23475         }),
23476         btn('list','insertunorderedlist',true);
23477         btn('pencil', false,true, function(btn){
23478                 Roo.log(this);
23479                 this.toggleSourceEdit(btn.pressed);
23480         });
23481         
23482         if (this.editor.btns.length > 0) {
23483             for (var i = 0; i<this.editor.btns.length; i++) {
23484                 children.push(this.editor.btns[i]);
23485             }
23486         }
23487         
23488         /*
23489         var cog = {
23490                 xtype: 'Button',
23491                 size : 'sm',
23492                 xns: Roo.bootstrap,
23493                 glyphicon : 'cog',
23494                 //html : 'submit'
23495                 menu : {
23496                     xtype: 'Menu',
23497                     xns: Roo.bootstrap,
23498                     items:  []
23499                 }
23500         };
23501         
23502         cog.menu.items.push({
23503             xtype :'MenuItem',
23504             xns: Roo.bootstrap,
23505             html : Clean styles,
23506             tagname : f,
23507             listeners : {
23508                 click : function()
23509                 {
23510                     editorcore.insertTag(this.tagname);
23511                     editor.focus();
23512                 }
23513             }
23514             
23515         });
23516        */
23517         
23518          
23519        this.xtype = 'NavSimplebar';
23520         
23521         for(var i=0;i< children.length;i++) {
23522             
23523             this.buttons.add(this.addxtypeChild(children[i]));
23524             
23525         }
23526         
23527         editor.on('editorevent', this.updateToolbar, this);
23528     },
23529     onBtnClick : function(id)
23530     {
23531        this.editorcore.relayCmd(id);
23532        this.editorcore.focus();
23533     },
23534     
23535     /**
23536      * Protected method that will not generally be called directly. It triggers
23537      * a toolbar update by reading the markup state of the current selection in the editor.
23538      */
23539     updateToolbar: function(){
23540
23541         if(!this.editorcore.activated){
23542             this.editor.onFirstFocus(); // is this neeed?
23543             return;
23544         }
23545
23546         var btns = this.buttons; 
23547         var doc = this.editorcore.doc;
23548         btns.get('bold').setActive(doc.queryCommandState('bold'));
23549         btns.get('italic').setActive(doc.queryCommandState('italic'));
23550         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23551         
23552         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23553         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23554         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23555         
23556         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23557         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23558          /*
23559         
23560         var ans = this.editorcore.getAllAncestors();
23561         if (this.formatCombo) {
23562             
23563             
23564             var store = this.formatCombo.store;
23565             this.formatCombo.setValue("");
23566             for (var i =0; i < ans.length;i++) {
23567                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23568                     // select it..
23569                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23570                     break;
23571                 }
23572             }
23573         }
23574         
23575         
23576         
23577         // hides menus... - so this cant be on a menu...
23578         Roo.bootstrap.MenuMgr.hideAll();
23579         */
23580         Roo.bootstrap.MenuMgr.hideAll();
23581         //this.editorsyncValue();
23582     },
23583     onFirstFocus: function() {
23584         this.buttons.each(function(item){
23585            item.enable();
23586         });
23587     },
23588     toggleSourceEdit : function(sourceEditMode){
23589         
23590           
23591         if(sourceEditMode){
23592             Roo.log("disabling buttons");
23593            this.buttons.each( function(item){
23594                 if(item.cmd != 'pencil'){
23595                     item.disable();
23596                 }
23597             });
23598           
23599         }else{
23600             Roo.log("enabling buttons");
23601             if(this.editorcore.initialized){
23602                 this.buttons.each( function(item){
23603                     item.enable();
23604                 });
23605             }
23606             
23607         }
23608         Roo.log("calling toggole on editor");
23609         // tell the editor that it's been pressed..
23610         this.editor.toggleSourceEdit(sourceEditMode);
23611        
23612     }
23613 });
23614
23615
23616
23617
23618
23619 /**
23620  * @class Roo.bootstrap.Table.AbstractSelectionModel
23621  * @extends Roo.util.Observable
23622  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23623  * implemented by descendant classes.  This class should not be directly instantiated.
23624  * @constructor
23625  */
23626 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23627     this.locked = false;
23628     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23629 };
23630
23631
23632 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23633     /** @ignore Called by the grid automatically. Do not call directly. */
23634     init : function(grid){
23635         this.grid = grid;
23636         this.initEvents();
23637     },
23638
23639     /**
23640      * Locks the selections.
23641      */
23642     lock : function(){
23643         this.locked = true;
23644     },
23645
23646     /**
23647      * Unlocks the selections.
23648      */
23649     unlock : function(){
23650         this.locked = false;
23651     },
23652
23653     /**
23654      * Returns true if the selections are locked.
23655      * @return {Boolean}
23656      */
23657     isLocked : function(){
23658         return this.locked;
23659     }
23660 });
23661 /**
23662  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23663  * @class Roo.bootstrap.Table.RowSelectionModel
23664  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23665  * It supports multiple selections and keyboard selection/navigation. 
23666  * @constructor
23667  * @param {Object} config
23668  */
23669
23670 Roo.bootstrap.Table.RowSelectionModel = function(config){
23671     Roo.apply(this, config);
23672     this.selections = new Roo.util.MixedCollection(false, function(o){
23673         return o.id;
23674     });
23675
23676     this.last = false;
23677     this.lastActive = false;
23678
23679     this.addEvents({
23680         /**
23681              * @event selectionchange
23682              * Fires when the selection changes
23683              * @param {SelectionModel} this
23684              */
23685             "selectionchange" : true,
23686         /**
23687              * @event afterselectionchange
23688              * Fires after the selection changes (eg. by key press or clicking)
23689              * @param {SelectionModel} this
23690              */
23691             "afterselectionchange" : true,
23692         /**
23693              * @event beforerowselect
23694              * Fires when a row is selected being selected, return false to cancel.
23695              * @param {SelectionModel} this
23696              * @param {Number} rowIndex The selected index
23697              * @param {Boolean} keepExisting False if other selections will be cleared
23698              */
23699             "beforerowselect" : true,
23700         /**
23701              * @event rowselect
23702              * Fires when a row is selected.
23703              * @param {SelectionModel} this
23704              * @param {Number} rowIndex The selected index
23705              * @param {Roo.data.Record} r The record
23706              */
23707             "rowselect" : true,
23708         /**
23709              * @event rowdeselect
23710              * Fires when a row is deselected.
23711              * @param {SelectionModel} this
23712              * @param {Number} rowIndex The selected index
23713              */
23714         "rowdeselect" : true
23715     });
23716     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23717     this.locked = false;
23718  };
23719
23720 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23721     /**
23722      * @cfg {Boolean} singleSelect
23723      * True to allow selection of only one row at a time (defaults to false)
23724      */
23725     singleSelect : false,
23726
23727     // private
23728     initEvents : function()
23729     {
23730
23731         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23732         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23733         //}else{ // allow click to work like normal
23734          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23735         //}
23736         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23737         this.grid.on("rowclick", this.handleMouseDown, this);
23738         
23739         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23740             "up" : function(e){
23741                 if(!e.shiftKey){
23742                     this.selectPrevious(e.shiftKey);
23743                 }else if(this.last !== false && this.lastActive !== false){
23744                     var last = this.last;
23745                     this.selectRange(this.last,  this.lastActive-1);
23746                     this.grid.getView().focusRow(this.lastActive);
23747                     if(last !== false){
23748                         this.last = last;
23749                     }
23750                 }else{
23751                     this.selectFirstRow();
23752                 }
23753                 this.fireEvent("afterselectionchange", this);
23754             },
23755             "down" : function(e){
23756                 if(!e.shiftKey){
23757                     this.selectNext(e.shiftKey);
23758                 }else if(this.last !== false && this.lastActive !== false){
23759                     var last = this.last;
23760                     this.selectRange(this.last,  this.lastActive+1);
23761                     this.grid.getView().focusRow(this.lastActive);
23762                     if(last !== false){
23763                         this.last = last;
23764                     }
23765                 }else{
23766                     this.selectFirstRow();
23767                 }
23768                 this.fireEvent("afterselectionchange", this);
23769             },
23770             scope: this
23771         });
23772         this.grid.store.on('load', function(){
23773             this.selections.clear();
23774         },this);
23775         /*
23776         var view = this.grid.view;
23777         view.on("refresh", this.onRefresh, this);
23778         view.on("rowupdated", this.onRowUpdated, this);
23779         view.on("rowremoved", this.onRemove, this);
23780         */
23781     },
23782
23783     // private
23784     onRefresh : function()
23785     {
23786         var ds = this.grid.store, i, v = this.grid.view;
23787         var s = this.selections;
23788         s.each(function(r){
23789             if((i = ds.indexOfId(r.id)) != -1){
23790                 v.onRowSelect(i);
23791             }else{
23792                 s.remove(r);
23793             }
23794         });
23795     },
23796
23797     // private
23798     onRemove : function(v, index, r){
23799         this.selections.remove(r);
23800     },
23801
23802     // private
23803     onRowUpdated : function(v, index, r){
23804         if(this.isSelected(r)){
23805             v.onRowSelect(index);
23806         }
23807     },
23808
23809     /**
23810      * Select records.
23811      * @param {Array} records The records to select
23812      * @param {Boolean} keepExisting (optional) True to keep existing selections
23813      */
23814     selectRecords : function(records, keepExisting)
23815     {
23816         if(!keepExisting){
23817             this.clearSelections();
23818         }
23819             var ds = this.grid.store;
23820         for(var i = 0, len = records.length; i < len; i++){
23821             this.selectRow(ds.indexOf(records[i]), true);
23822         }
23823     },
23824
23825     /**
23826      * Gets the number of selected rows.
23827      * @return {Number}
23828      */
23829     getCount : function(){
23830         return this.selections.length;
23831     },
23832
23833     /**
23834      * Selects the first row in the grid.
23835      */
23836     selectFirstRow : function(){
23837         this.selectRow(0);
23838     },
23839
23840     /**
23841      * Select the last row.
23842      * @param {Boolean} keepExisting (optional) True to keep existing selections
23843      */
23844     selectLastRow : function(keepExisting){
23845         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23846         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23847     },
23848
23849     /**
23850      * Selects the row immediately following the last selected row.
23851      * @param {Boolean} keepExisting (optional) True to keep existing selections
23852      */
23853     selectNext : function(keepExisting)
23854     {
23855             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23856             this.selectRow(this.last+1, keepExisting);
23857             this.grid.getView().focusRow(this.last);
23858         }
23859     },
23860
23861     /**
23862      * Selects the row that precedes the last selected row.
23863      * @param {Boolean} keepExisting (optional) True to keep existing selections
23864      */
23865     selectPrevious : function(keepExisting){
23866         if(this.last){
23867             this.selectRow(this.last-1, keepExisting);
23868             this.grid.getView().focusRow(this.last);
23869         }
23870     },
23871
23872     /**
23873      * Returns the selected records
23874      * @return {Array} Array of selected records
23875      */
23876     getSelections : function(){
23877         return [].concat(this.selections.items);
23878     },
23879
23880     /**
23881      * Returns the first selected record.
23882      * @return {Record}
23883      */
23884     getSelected : function(){
23885         return this.selections.itemAt(0);
23886     },
23887
23888
23889     /**
23890      * Clears all selections.
23891      */
23892     clearSelections : function(fast)
23893     {
23894         if(this.locked) {
23895             return;
23896         }
23897         if(fast !== true){
23898                 var ds = this.grid.store;
23899             var s = this.selections;
23900             s.each(function(r){
23901                 this.deselectRow(ds.indexOfId(r.id));
23902             }, this);
23903             s.clear();
23904         }else{
23905             this.selections.clear();
23906         }
23907         this.last = false;
23908     },
23909
23910
23911     /**
23912      * Selects all rows.
23913      */
23914     selectAll : function(){
23915         if(this.locked) {
23916             return;
23917         }
23918         this.selections.clear();
23919         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23920             this.selectRow(i, true);
23921         }
23922     },
23923
23924     /**
23925      * Returns True if there is a selection.
23926      * @return {Boolean}
23927      */
23928     hasSelection : function(){
23929         return this.selections.length > 0;
23930     },
23931
23932     /**
23933      * Returns True if the specified row is selected.
23934      * @param {Number/Record} record The record or index of the record to check
23935      * @return {Boolean}
23936      */
23937     isSelected : function(index){
23938             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23939         return (r && this.selections.key(r.id) ? true : false);
23940     },
23941
23942     /**
23943      * Returns True if the specified record id is selected.
23944      * @param {String} id The id of record to check
23945      * @return {Boolean}
23946      */
23947     isIdSelected : function(id){
23948         return (this.selections.key(id) ? true : false);
23949     },
23950
23951
23952     // private
23953     handleMouseDBClick : function(e, t){
23954         
23955     },
23956     // private
23957     handleMouseDown : function(e, t)
23958     {
23959             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23960         if(this.isLocked() || rowIndex < 0 ){
23961             return;
23962         };
23963         if(e.shiftKey && this.last !== false){
23964             var last = this.last;
23965             this.selectRange(last, rowIndex, e.ctrlKey);
23966             this.last = last; // reset the last
23967             t.focus();
23968     
23969         }else{
23970             var isSelected = this.isSelected(rowIndex);
23971             //Roo.log("select row:" + rowIndex);
23972             if(isSelected){
23973                 this.deselectRow(rowIndex);
23974             } else {
23975                         this.selectRow(rowIndex, true);
23976             }
23977     
23978             /*
23979                 if(e.button !== 0 && isSelected){
23980                 alert('rowIndex 2: ' + rowIndex);
23981                     view.focusRow(rowIndex);
23982                 }else if(e.ctrlKey && isSelected){
23983                     this.deselectRow(rowIndex);
23984                 }else if(!isSelected){
23985                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23986                     view.focusRow(rowIndex);
23987                 }
23988             */
23989         }
23990         this.fireEvent("afterselectionchange", this);
23991     },
23992     // private
23993     handleDragableRowClick :  function(grid, rowIndex, e) 
23994     {
23995         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23996             this.selectRow(rowIndex, false);
23997             grid.view.focusRow(rowIndex);
23998              this.fireEvent("afterselectionchange", this);
23999         }
24000     },
24001     
24002     /**
24003      * Selects multiple rows.
24004      * @param {Array} rows Array of the indexes of the row to select
24005      * @param {Boolean} keepExisting (optional) True to keep existing selections
24006      */
24007     selectRows : function(rows, keepExisting){
24008         if(!keepExisting){
24009             this.clearSelections();
24010         }
24011         for(var i = 0, len = rows.length; i < len; i++){
24012             this.selectRow(rows[i], true);
24013         }
24014     },
24015
24016     /**
24017      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24018      * @param {Number} startRow The index of the first row in the range
24019      * @param {Number} endRow The index of the last row in the range
24020      * @param {Boolean} keepExisting (optional) True to retain existing selections
24021      */
24022     selectRange : function(startRow, endRow, keepExisting){
24023         if(this.locked) {
24024             return;
24025         }
24026         if(!keepExisting){
24027             this.clearSelections();
24028         }
24029         if(startRow <= endRow){
24030             for(var i = startRow; i <= endRow; i++){
24031                 this.selectRow(i, true);
24032             }
24033         }else{
24034             for(var i = startRow; i >= endRow; i--){
24035                 this.selectRow(i, true);
24036             }
24037         }
24038     },
24039
24040     /**
24041      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24042      * @param {Number} startRow The index of the first row in the range
24043      * @param {Number} endRow The index of the last row in the range
24044      */
24045     deselectRange : function(startRow, endRow, preventViewNotify){
24046         if(this.locked) {
24047             return;
24048         }
24049         for(var i = startRow; i <= endRow; i++){
24050             this.deselectRow(i, preventViewNotify);
24051         }
24052     },
24053
24054     /**
24055      * Selects a row.
24056      * @param {Number} row The index of the row to select
24057      * @param {Boolean} keepExisting (optional) True to keep existing selections
24058      */
24059     selectRow : function(index, keepExisting, preventViewNotify)
24060     {
24061             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24062             return;
24063         }
24064         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24065             if(!keepExisting || this.singleSelect){
24066                 this.clearSelections();
24067             }
24068             
24069             var r = this.grid.store.getAt(index);
24070             //console.log('selectRow - record id :' + r.id);
24071             
24072             this.selections.add(r);
24073             this.last = this.lastActive = index;
24074             if(!preventViewNotify){
24075                 var proxy = new Roo.Element(
24076                                 this.grid.getRowDom(index)
24077                 );
24078                 proxy.addClass('bg-info info');
24079             }
24080             this.fireEvent("rowselect", this, index, r);
24081             this.fireEvent("selectionchange", this);
24082         }
24083     },
24084
24085     /**
24086      * Deselects a row.
24087      * @param {Number} row The index of the row to deselect
24088      */
24089     deselectRow : function(index, preventViewNotify)
24090     {
24091         if(this.locked) {
24092             return;
24093         }
24094         if(this.last == index){
24095             this.last = false;
24096         }
24097         if(this.lastActive == index){
24098             this.lastActive = false;
24099         }
24100         
24101         var r = this.grid.store.getAt(index);
24102         if (!r) {
24103             return;
24104         }
24105         
24106         this.selections.remove(r);
24107         //.console.log('deselectRow - record id :' + r.id);
24108         if(!preventViewNotify){
24109         
24110             var proxy = new Roo.Element(
24111                 this.grid.getRowDom(index)
24112             );
24113             proxy.removeClass('bg-info info');
24114         }
24115         this.fireEvent("rowdeselect", this, index);
24116         this.fireEvent("selectionchange", this);
24117     },
24118
24119     // private
24120     restoreLast : function(){
24121         if(this._last){
24122             this.last = this._last;
24123         }
24124     },
24125
24126     // private
24127     acceptsNav : function(row, col, cm){
24128         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24129     },
24130
24131     // private
24132     onEditorKey : function(field, e){
24133         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24134         if(k == e.TAB){
24135             e.stopEvent();
24136             ed.completeEdit();
24137             if(e.shiftKey){
24138                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24139             }else{
24140                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24141             }
24142         }else if(k == e.ENTER && !e.ctrlKey){
24143             e.stopEvent();
24144             ed.completeEdit();
24145             if(e.shiftKey){
24146                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24147             }else{
24148                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24149             }
24150         }else if(k == e.ESC){
24151             ed.cancelEdit();
24152         }
24153         if(newCell){
24154             g.startEditing(newCell[0], newCell[1]);
24155         }
24156     }
24157 });
24158 /*
24159  * Based on:
24160  * Ext JS Library 1.1.1
24161  * Copyright(c) 2006-2007, Ext JS, LLC.
24162  *
24163  * Originally Released Under LGPL - original licence link has changed is not relivant.
24164  *
24165  * Fork - LGPL
24166  * <script type="text/javascript">
24167  */
24168  
24169 /**
24170  * @class Roo.bootstrap.PagingToolbar
24171  * @extends Roo.bootstrap.NavSimplebar
24172  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24173  * @constructor
24174  * Create a new PagingToolbar
24175  * @param {Object} config The config object
24176  * @param {Roo.data.Store} store
24177  */
24178 Roo.bootstrap.PagingToolbar = function(config)
24179 {
24180     // old args format still supported... - xtype is prefered..
24181         // created from xtype...
24182     
24183     this.ds = config.dataSource;
24184     
24185     if (config.store && !this.ds) {
24186         this.store= Roo.factory(config.store, Roo.data);
24187         this.ds = this.store;
24188         this.ds.xmodule = this.xmodule || false;
24189     }
24190     
24191     this.toolbarItems = [];
24192     if (config.items) {
24193         this.toolbarItems = config.items;
24194     }
24195     
24196     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24197     
24198     this.cursor = 0;
24199     
24200     if (this.ds) { 
24201         this.bind(this.ds);
24202     }
24203     
24204     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24205     
24206 };
24207
24208 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24209     /**
24210      * @cfg {Roo.data.Store} dataSource
24211      * The underlying data store providing the paged data
24212      */
24213     /**
24214      * @cfg {String/HTMLElement/Element} container
24215      * container The id or element that will contain the toolbar
24216      */
24217     /**
24218      * @cfg {Boolean} displayInfo
24219      * True to display the displayMsg (defaults to false)
24220      */
24221     /**
24222      * @cfg {Number} pageSize
24223      * The number of records to display per page (defaults to 20)
24224      */
24225     pageSize: 20,
24226     /**
24227      * @cfg {String} displayMsg
24228      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24229      */
24230     displayMsg : 'Displaying {0} - {1} of {2}',
24231     /**
24232      * @cfg {String} emptyMsg
24233      * The message to display when no records are found (defaults to "No data to display")
24234      */
24235     emptyMsg : 'No data to display',
24236     /**
24237      * Customizable piece of the default paging text (defaults to "Page")
24238      * @type String
24239      */
24240     beforePageText : "Page",
24241     /**
24242      * Customizable piece of the default paging text (defaults to "of %0")
24243      * @type String
24244      */
24245     afterPageText : "of {0}",
24246     /**
24247      * Customizable piece of the default paging text (defaults to "First Page")
24248      * @type String
24249      */
24250     firstText : "First Page",
24251     /**
24252      * Customizable piece of the default paging text (defaults to "Previous Page")
24253      * @type String
24254      */
24255     prevText : "Previous Page",
24256     /**
24257      * Customizable piece of the default paging text (defaults to "Next Page")
24258      * @type String
24259      */
24260     nextText : "Next Page",
24261     /**
24262      * Customizable piece of the default paging text (defaults to "Last Page")
24263      * @type String
24264      */
24265     lastText : "Last Page",
24266     /**
24267      * Customizable piece of the default paging text (defaults to "Refresh")
24268      * @type String
24269      */
24270     refreshText : "Refresh",
24271
24272     buttons : false,
24273     // private
24274     onRender : function(ct, position) 
24275     {
24276         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24277         this.navgroup.parentId = this.id;
24278         this.navgroup.onRender(this.el, null);
24279         // add the buttons to the navgroup
24280         
24281         if(this.displayInfo){
24282             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24283             this.displayEl = this.el.select('.x-paging-info', true).first();
24284 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24285 //            this.displayEl = navel.el.select('span',true).first();
24286         }
24287         
24288         var _this = this;
24289         
24290         if(this.buttons){
24291             Roo.each(_this.buttons, function(e){ // this might need to use render????
24292                Roo.factory(e).onRender(_this.el, null);
24293             });
24294         }
24295             
24296         Roo.each(_this.toolbarItems, function(e) {
24297             _this.navgroup.addItem(e);
24298         });
24299         
24300         
24301         this.first = this.navgroup.addItem({
24302             tooltip: this.firstText,
24303             cls: "prev",
24304             icon : 'fa fa-backward',
24305             disabled: true,
24306             preventDefault: true,
24307             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24308         });
24309         
24310         this.prev =  this.navgroup.addItem({
24311             tooltip: this.prevText,
24312             cls: "prev",
24313             icon : 'fa fa-step-backward',
24314             disabled: true,
24315             preventDefault: true,
24316             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24317         });
24318     //this.addSeparator();
24319         
24320         
24321         var field = this.navgroup.addItem( {
24322             tagtype : 'span',
24323             cls : 'x-paging-position',
24324             
24325             html : this.beforePageText  +
24326                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24327                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24328          } ); //?? escaped?
24329         
24330         this.field = field.el.select('input', true).first();
24331         this.field.on("keydown", this.onPagingKeydown, this);
24332         this.field.on("focus", function(){this.dom.select();});
24333     
24334     
24335         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24336         //this.field.setHeight(18);
24337         //this.addSeparator();
24338         this.next = this.navgroup.addItem({
24339             tooltip: this.nextText,
24340             cls: "next",
24341             html : ' <i class="fa fa-step-forward">',
24342             disabled: true,
24343             preventDefault: true,
24344             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24345         });
24346         this.last = this.navgroup.addItem({
24347             tooltip: this.lastText,
24348             icon : 'fa fa-forward',
24349             cls: "next",
24350             disabled: true,
24351             preventDefault: true,
24352             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24353         });
24354     //this.addSeparator();
24355         this.loading = this.navgroup.addItem({
24356             tooltip: this.refreshText,
24357             icon: 'fa fa-refresh',
24358             preventDefault: true,
24359             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24360         });
24361         
24362     },
24363
24364     // private
24365     updateInfo : function(){
24366         if(this.displayEl){
24367             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24368             var msg = count == 0 ?
24369                 this.emptyMsg :
24370                 String.format(
24371                     this.displayMsg,
24372                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24373                 );
24374             this.displayEl.update(msg);
24375         }
24376     },
24377
24378     // private
24379     onLoad : function(ds, r, o)
24380     {
24381         this.cursor = o.params ? o.params.start : 0;
24382         var d = this.getPageData(),
24383             ap = d.activePage,
24384             ps = d.pages;
24385         
24386         
24387         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24388         this.field.dom.value = ap;
24389         this.first.setDisabled(ap == 1);
24390         this.prev.setDisabled(ap == 1);
24391         this.next.setDisabled(ap == ps);
24392         this.last.setDisabled(ap == ps);
24393         this.loading.enable();
24394         this.updateInfo();
24395     },
24396
24397     // private
24398     getPageData : function(){
24399         var total = this.ds.getTotalCount();
24400         return {
24401             total : total,
24402             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24403             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24404         };
24405     },
24406
24407     // private
24408     onLoadError : function(){
24409         this.loading.enable();
24410     },
24411
24412     // private
24413     onPagingKeydown : function(e){
24414         var k = e.getKey();
24415         var d = this.getPageData();
24416         if(k == e.RETURN){
24417             var v = this.field.dom.value, pageNum;
24418             if(!v || isNaN(pageNum = parseInt(v, 10))){
24419                 this.field.dom.value = d.activePage;
24420                 return;
24421             }
24422             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24423             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24424             e.stopEvent();
24425         }
24426         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))
24427         {
24428           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24429           this.field.dom.value = pageNum;
24430           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24431           e.stopEvent();
24432         }
24433         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24434         {
24435           var v = this.field.dom.value, pageNum; 
24436           var increment = (e.shiftKey) ? 10 : 1;
24437           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24438                 increment *= -1;
24439           }
24440           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24441             this.field.dom.value = d.activePage;
24442             return;
24443           }
24444           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24445           {
24446             this.field.dom.value = parseInt(v, 10) + increment;
24447             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24448             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24449           }
24450           e.stopEvent();
24451         }
24452     },
24453
24454     // private
24455     beforeLoad : function(){
24456         if(this.loading){
24457             this.loading.disable();
24458         }
24459     },
24460
24461     // private
24462     onClick : function(which){
24463         
24464         var ds = this.ds;
24465         if (!ds) {
24466             return;
24467         }
24468         
24469         switch(which){
24470             case "first":
24471                 ds.load({params:{start: 0, limit: this.pageSize}});
24472             break;
24473             case "prev":
24474                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24475             break;
24476             case "next":
24477                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24478             break;
24479             case "last":
24480                 var total = ds.getTotalCount();
24481                 var extra = total % this.pageSize;
24482                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24483                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24484             break;
24485             case "refresh":
24486                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24487             break;
24488         }
24489     },
24490
24491     /**
24492      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24493      * @param {Roo.data.Store} store The data store to unbind
24494      */
24495     unbind : function(ds){
24496         ds.un("beforeload", this.beforeLoad, this);
24497         ds.un("load", this.onLoad, this);
24498         ds.un("loadexception", this.onLoadError, this);
24499         ds.un("remove", this.updateInfo, this);
24500         ds.un("add", this.updateInfo, this);
24501         this.ds = undefined;
24502     },
24503
24504     /**
24505      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24506      * @param {Roo.data.Store} store The data store to bind
24507      */
24508     bind : function(ds){
24509         ds.on("beforeload", this.beforeLoad, this);
24510         ds.on("load", this.onLoad, this);
24511         ds.on("loadexception", this.onLoadError, this);
24512         ds.on("remove", this.updateInfo, this);
24513         ds.on("add", this.updateInfo, this);
24514         this.ds = ds;
24515     }
24516 });/*
24517  * - LGPL
24518  *
24519  * element
24520  * 
24521  */
24522
24523 /**
24524  * @class Roo.bootstrap.MessageBar
24525  * @extends Roo.bootstrap.Component
24526  * Bootstrap MessageBar class
24527  * @cfg {String} html contents of the MessageBar
24528  * @cfg {String} weight (info | success | warning | danger) default info
24529  * @cfg {String} beforeClass insert the bar before the given class
24530  * @cfg {Boolean} closable (true | false) default false
24531  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24532  * 
24533  * @constructor
24534  * Create a new Element
24535  * @param {Object} config The config object
24536  */
24537
24538 Roo.bootstrap.MessageBar = function(config){
24539     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24540 };
24541
24542 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24543     
24544     html: '',
24545     weight: 'info',
24546     closable: false,
24547     fixed: false,
24548     beforeClass: 'bootstrap-sticky-wrap',
24549     
24550     getAutoCreate : function(){
24551         
24552         var cfg = {
24553             tag: 'div',
24554             cls: 'alert alert-dismissable alert-' + this.weight,
24555             cn: [
24556                 {
24557                     tag: 'span',
24558                     cls: 'message',
24559                     html: this.html || ''
24560                 }
24561             ]
24562         };
24563         
24564         if(this.fixed){
24565             cfg.cls += ' alert-messages-fixed';
24566         }
24567         
24568         if(this.closable){
24569             cfg.cn.push({
24570                 tag: 'button',
24571                 cls: 'close',
24572                 html: 'x'
24573             });
24574         }
24575         
24576         return cfg;
24577     },
24578     
24579     onRender : function(ct, position)
24580     {
24581         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24582         
24583         if(!this.el){
24584             var cfg = Roo.apply({},  this.getAutoCreate());
24585             cfg.id = Roo.id();
24586             
24587             if (this.cls) {
24588                 cfg.cls += ' ' + this.cls;
24589             }
24590             if (this.style) {
24591                 cfg.style = this.style;
24592             }
24593             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24594             
24595             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24596         }
24597         
24598         this.el.select('>button.close').on('click', this.hide, this);
24599         
24600     },
24601     
24602     show : function()
24603     {
24604         if (!this.rendered) {
24605             this.render();
24606         }
24607         
24608         this.el.show();
24609         
24610         this.fireEvent('show', this);
24611         
24612     },
24613     
24614     hide : function()
24615     {
24616         if (!this.rendered) {
24617             this.render();
24618         }
24619         
24620         this.el.hide();
24621         
24622         this.fireEvent('hide', this);
24623     },
24624     
24625     update : function()
24626     {
24627 //        var e = this.el.dom.firstChild;
24628 //        
24629 //        if(this.closable){
24630 //            e = e.nextSibling;
24631 //        }
24632 //        
24633 //        e.data = this.html || '';
24634
24635         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24636     }
24637    
24638 });
24639
24640  
24641
24642      /*
24643  * - LGPL
24644  *
24645  * Graph
24646  * 
24647  */
24648
24649
24650 /**
24651  * @class Roo.bootstrap.Graph
24652  * @extends Roo.bootstrap.Component
24653  * Bootstrap Graph class
24654 > Prameters
24655  -sm {number} sm 4
24656  -md {number} md 5
24657  @cfg {String} graphtype  bar | vbar | pie
24658  @cfg {number} g_x coodinator | centre x (pie)
24659  @cfg {number} g_y coodinator | centre y (pie)
24660  @cfg {number} g_r radius (pie)
24661  @cfg {number} g_height height of the chart (respected by all elements in the set)
24662  @cfg {number} g_width width of the chart (respected by all elements in the set)
24663  @cfg {Object} title The title of the chart
24664     
24665  -{Array}  values
24666  -opts (object) options for the chart 
24667      o {
24668      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24669      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24670      o vgutter (number)
24671      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.
24672      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24673      o to
24674      o stretch (boolean)
24675      o }
24676  -opts (object) options for the pie
24677      o{
24678      o cut
24679      o startAngle (number)
24680      o endAngle (number)
24681      } 
24682  *
24683  * @constructor
24684  * Create a new Input
24685  * @param {Object} config The config object
24686  */
24687
24688 Roo.bootstrap.Graph = function(config){
24689     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24690     
24691     this.addEvents({
24692         // img events
24693         /**
24694          * @event click
24695          * The img click event for the img.
24696          * @param {Roo.EventObject} e
24697          */
24698         "click" : true
24699     });
24700 };
24701
24702 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24703     
24704     sm: 4,
24705     md: 5,
24706     graphtype: 'bar',
24707     g_height: 250,
24708     g_width: 400,
24709     g_x: 50,
24710     g_y: 50,
24711     g_r: 30,
24712     opts:{
24713         //g_colors: this.colors,
24714         g_type: 'soft',
24715         g_gutter: '20%'
24716
24717     },
24718     title : false,
24719
24720     getAutoCreate : function(){
24721         
24722         var cfg = {
24723             tag: 'div',
24724             html : null
24725         };
24726         
24727         
24728         return  cfg;
24729     },
24730
24731     onRender : function(ct,position){
24732         
24733         
24734         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24735         
24736         if (typeof(Raphael) == 'undefined') {
24737             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24738             return;
24739         }
24740         
24741         this.raphael = Raphael(this.el.dom);
24742         
24743                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24744                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24745                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24746                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24747                 /*
24748                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24749                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24750                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24751                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24752                 
24753                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24754                 r.barchart(330, 10, 300, 220, data1);
24755                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24756                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24757                 */
24758                 
24759                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24760                 // r.barchart(30, 30, 560, 250,  xdata, {
24761                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24762                 //     axis : "0 0 1 1",
24763                 //     axisxlabels :  xdata
24764                 //     //yvalues : cols,
24765                    
24766                 // });
24767 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24768 //        
24769 //        this.load(null,xdata,{
24770 //                axis : "0 0 1 1",
24771 //                axisxlabels :  xdata
24772 //                });
24773
24774     },
24775
24776     load : function(graphtype,xdata,opts)
24777     {
24778         this.raphael.clear();
24779         if(!graphtype) {
24780             graphtype = this.graphtype;
24781         }
24782         if(!opts){
24783             opts = this.opts;
24784         }
24785         var r = this.raphael,
24786             fin = function () {
24787                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24788             },
24789             fout = function () {
24790                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24791             },
24792             pfin = function() {
24793                 this.sector.stop();
24794                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24795
24796                 if (this.label) {
24797                     this.label[0].stop();
24798                     this.label[0].attr({ r: 7.5 });
24799                     this.label[1].attr({ "font-weight": 800 });
24800                 }
24801             },
24802             pfout = function() {
24803                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24804
24805                 if (this.label) {
24806                     this.label[0].animate({ r: 5 }, 500, "bounce");
24807                     this.label[1].attr({ "font-weight": 400 });
24808                 }
24809             };
24810
24811         switch(graphtype){
24812             case 'bar':
24813                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24814                 break;
24815             case 'hbar':
24816                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24817                 break;
24818             case 'pie':
24819 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24820 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24821 //            
24822                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24823                 
24824                 break;
24825
24826         }
24827         
24828         if(this.title){
24829             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24830         }
24831         
24832     },
24833     
24834     setTitle: function(o)
24835     {
24836         this.title = o;
24837     },
24838     
24839     initEvents: function() {
24840         
24841         if(!this.href){
24842             this.el.on('click', this.onClick, this);
24843         }
24844     },
24845     
24846     onClick : function(e)
24847     {
24848         Roo.log('img onclick');
24849         this.fireEvent('click', this, e);
24850     }
24851    
24852 });
24853
24854  
24855 /*
24856  * - LGPL
24857  *
24858  * numberBox
24859  * 
24860  */
24861 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24862
24863 /**
24864  * @class Roo.bootstrap.dash.NumberBox
24865  * @extends Roo.bootstrap.Component
24866  * Bootstrap NumberBox class
24867  * @cfg {String} headline Box headline
24868  * @cfg {String} content Box content
24869  * @cfg {String} icon Box icon
24870  * @cfg {String} footer Footer text
24871  * @cfg {String} fhref Footer href
24872  * 
24873  * @constructor
24874  * Create a new NumberBox
24875  * @param {Object} config The config object
24876  */
24877
24878
24879 Roo.bootstrap.dash.NumberBox = function(config){
24880     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24881     
24882 };
24883
24884 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24885     
24886     headline : '',
24887     content : '',
24888     icon : '',
24889     footer : '',
24890     fhref : '',
24891     ficon : '',
24892     
24893     getAutoCreate : function(){
24894         
24895         var cfg = {
24896             tag : 'div',
24897             cls : 'small-box ',
24898             cn : [
24899                 {
24900                     tag : 'div',
24901                     cls : 'inner',
24902                     cn :[
24903                         {
24904                             tag : 'h3',
24905                             cls : 'roo-headline',
24906                             html : this.headline
24907                         },
24908                         {
24909                             tag : 'p',
24910                             cls : 'roo-content',
24911                             html : this.content
24912                         }
24913                     ]
24914                 }
24915             ]
24916         };
24917         
24918         if(this.icon){
24919             cfg.cn.push({
24920                 tag : 'div',
24921                 cls : 'icon',
24922                 cn :[
24923                     {
24924                         tag : 'i',
24925                         cls : 'ion ' + this.icon
24926                     }
24927                 ]
24928             });
24929         }
24930         
24931         if(this.footer){
24932             var footer = {
24933                 tag : 'a',
24934                 cls : 'small-box-footer',
24935                 href : this.fhref || '#',
24936                 html : this.footer
24937             };
24938             
24939             cfg.cn.push(footer);
24940             
24941         }
24942         
24943         return  cfg;
24944     },
24945
24946     onRender : function(ct,position){
24947         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24948
24949
24950        
24951                 
24952     },
24953
24954     setHeadline: function (value)
24955     {
24956         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24957     },
24958     
24959     setFooter: function (value, href)
24960     {
24961         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24962         
24963         if(href){
24964             this.el.select('a.small-box-footer',true).first().attr('href', href);
24965         }
24966         
24967     },
24968
24969     setContent: function (value)
24970     {
24971         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24972     },
24973
24974     initEvents: function() 
24975     {   
24976         
24977     }
24978     
24979 });
24980
24981  
24982 /*
24983  * - LGPL
24984  *
24985  * TabBox
24986  * 
24987  */
24988 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24989
24990 /**
24991  * @class Roo.bootstrap.dash.TabBox
24992  * @extends Roo.bootstrap.Component
24993  * Bootstrap TabBox class
24994  * @cfg {String} title Title of the TabBox
24995  * @cfg {String} icon Icon of the TabBox
24996  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24997  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24998  * 
24999  * @constructor
25000  * Create a new TabBox
25001  * @param {Object} config The config object
25002  */
25003
25004
25005 Roo.bootstrap.dash.TabBox = function(config){
25006     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25007     this.addEvents({
25008         // raw events
25009         /**
25010          * @event addpane
25011          * When a pane is added
25012          * @param {Roo.bootstrap.dash.TabPane} pane
25013          */
25014         "addpane" : true,
25015         /**
25016          * @event activatepane
25017          * When a pane is activated
25018          * @param {Roo.bootstrap.dash.TabPane} pane
25019          */
25020         "activatepane" : true
25021         
25022          
25023     });
25024     
25025     this.panes = [];
25026 };
25027
25028 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25029
25030     title : '',
25031     icon : false,
25032     showtabs : true,
25033     tabScrollable : false,
25034     
25035     getChildContainer : function()
25036     {
25037         return this.el.select('.tab-content', true).first();
25038     },
25039     
25040     getAutoCreate : function(){
25041         
25042         var header = {
25043             tag: 'li',
25044             cls: 'pull-left header',
25045             html: this.title,
25046             cn : []
25047         };
25048         
25049         if(this.icon){
25050             header.cn.push({
25051                 tag: 'i',
25052                 cls: 'fa ' + this.icon
25053             });
25054         }
25055         
25056         var h = {
25057             tag: 'ul',
25058             cls: 'nav nav-tabs pull-right',
25059             cn: [
25060                 header
25061             ]
25062         };
25063         
25064         if(this.tabScrollable){
25065             h = {
25066                 tag: 'div',
25067                 cls: 'tab-header',
25068                 cn: [
25069                     {
25070                         tag: 'ul',
25071                         cls: 'nav nav-tabs pull-right',
25072                         cn: [
25073                             header
25074                         ]
25075                     }
25076                 ]
25077             };
25078         }
25079         
25080         var cfg = {
25081             tag: 'div',
25082             cls: 'nav-tabs-custom',
25083             cn: [
25084                 h,
25085                 {
25086                     tag: 'div',
25087                     cls: 'tab-content no-padding',
25088                     cn: []
25089                 }
25090             ]
25091         };
25092
25093         return  cfg;
25094     },
25095     initEvents : function()
25096     {
25097         //Roo.log('add add pane handler');
25098         this.on('addpane', this.onAddPane, this);
25099     },
25100      /**
25101      * Updates the box title
25102      * @param {String} html to set the title to.
25103      */
25104     setTitle : function(value)
25105     {
25106         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25107     },
25108     onAddPane : function(pane)
25109     {
25110         this.panes.push(pane);
25111         //Roo.log('addpane');
25112         //Roo.log(pane);
25113         // tabs are rendere left to right..
25114         if(!this.showtabs){
25115             return;
25116         }
25117         
25118         var ctr = this.el.select('.nav-tabs', true).first();
25119          
25120          
25121         var existing = ctr.select('.nav-tab',true);
25122         var qty = existing.getCount();;
25123         
25124         
25125         var tab = ctr.createChild({
25126             tag : 'li',
25127             cls : 'nav-tab' + (qty ? '' : ' active'),
25128             cn : [
25129                 {
25130                     tag : 'a',
25131                     href:'#',
25132                     html : pane.title
25133                 }
25134             ]
25135         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25136         pane.tab = tab;
25137         
25138         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25139         if (!qty) {
25140             pane.el.addClass('active');
25141         }
25142         
25143                 
25144     },
25145     onTabClick : function(ev,un,ob,pane)
25146     {
25147         //Roo.log('tab - prev default');
25148         ev.preventDefault();
25149         
25150         
25151         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25152         pane.tab.addClass('active');
25153         //Roo.log(pane.title);
25154         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25155         // technically we should have a deactivate event.. but maybe add later.
25156         // and it should not de-activate the selected tab...
25157         this.fireEvent('activatepane', pane);
25158         pane.el.addClass('active');
25159         pane.fireEvent('activate');
25160         
25161         
25162     },
25163     
25164     getActivePane : function()
25165     {
25166         var r = false;
25167         Roo.each(this.panes, function(p) {
25168             if(p.el.hasClass('active')){
25169                 r = p;
25170                 return false;
25171             }
25172             
25173             return;
25174         });
25175         
25176         return r;
25177     }
25178     
25179     
25180 });
25181
25182  
25183 /*
25184  * - LGPL
25185  *
25186  * Tab pane
25187  * 
25188  */
25189 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25190 /**
25191  * @class Roo.bootstrap.TabPane
25192  * @extends Roo.bootstrap.Component
25193  * Bootstrap TabPane class
25194  * @cfg {Boolean} active (false | true) Default false
25195  * @cfg {String} title title of panel
25196
25197  * 
25198  * @constructor
25199  * Create a new TabPane
25200  * @param {Object} config The config object
25201  */
25202
25203 Roo.bootstrap.dash.TabPane = function(config){
25204     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25205     
25206     this.addEvents({
25207         // raw events
25208         /**
25209          * @event activate
25210          * When a pane is activated
25211          * @param {Roo.bootstrap.dash.TabPane} pane
25212          */
25213         "activate" : true
25214          
25215     });
25216 };
25217
25218 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25219     
25220     active : false,
25221     title : '',
25222     
25223     // the tabBox that this is attached to.
25224     tab : false,
25225      
25226     getAutoCreate : function() 
25227     {
25228         var cfg = {
25229             tag: 'div',
25230             cls: 'tab-pane'
25231         };
25232         
25233         if(this.active){
25234             cfg.cls += ' active';
25235         }
25236         
25237         return cfg;
25238     },
25239     initEvents  : function()
25240     {
25241         //Roo.log('trigger add pane handler');
25242         this.parent().fireEvent('addpane', this)
25243     },
25244     
25245      /**
25246      * Updates the tab title 
25247      * @param {String} html to set the title to.
25248      */
25249     setTitle: function(str)
25250     {
25251         if (!this.tab) {
25252             return;
25253         }
25254         this.title = str;
25255         this.tab.select('a', true).first().dom.innerHTML = str;
25256         
25257     }
25258     
25259     
25260     
25261 });
25262
25263  
25264
25265
25266  /*
25267  * - LGPL
25268  *
25269  * menu
25270  * 
25271  */
25272 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25273
25274 /**
25275  * @class Roo.bootstrap.menu.Menu
25276  * @extends Roo.bootstrap.Component
25277  * Bootstrap Menu class - container for Menu
25278  * @cfg {String} html Text of the menu
25279  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25280  * @cfg {String} icon Font awesome icon
25281  * @cfg {String} pos Menu align to (top | bottom) default bottom
25282  * 
25283  * 
25284  * @constructor
25285  * Create a new Menu
25286  * @param {Object} config The config object
25287  */
25288
25289
25290 Roo.bootstrap.menu.Menu = function(config){
25291     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25292     
25293     this.addEvents({
25294         /**
25295          * @event beforeshow
25296          * Fires before this menu is displayed
25297          * @param {Roo.bootstrap.menu.Menu} this
25298          */
25299         beforeshow : true,
25300         /**
25301          * @event beforehide
25302          * Fires before this menu is hidden
25303          * @param {Roo.bootstrap.menu.Menu} this
25304          */
25305         beforehide : true,
25306         /**
25307          * @event show
25308          * Fires after this menu is displayed
25309          * @param {Roo.bootstrap.menu.Menu} this
25310          */
25311         show : true,
25312         /**
25313          * @event hide
25314          * Fires after this menu is hidden
25315          * @param {Roo.bootstrap.menu.Menu} this
25316          */
25317         hide : true,
25318         /**
25319          * @event click
25320          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25321          * @param {Roo.bootstrap.menu.Menu} this
25322          * @param {Roo.EventObject} e
25323          */
25324         click : true
25325     });
25326     
25327 };
25328
25329 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25330     
25331     submenu : false,
25332     html : '',
25333     weight : 'default',
25334     icon : false,
25335     pos : 'bottom',
25336     
25337     
25338     getChildContainer : function() {
25339         if(this.isSubMenu){
25340             return this.el;
25341         }
25342         
25343         return this.el.select('ul.dropdown-menu', true).first();  
25344     },
25345     
25346     getAutoCreate : function()
25347     {
25348         var text = [
25349             {
25350                 tag : 'span',
25351                 cls : 'roo-menu-text',
25352                 html : this.html
25353             }
25354         ];
25355         
25356         if(this.icon){
25357             text.unshift({
25358                 tag : 'i',
25359                 cls : 'fa ' + this.icon
25360             })
25361         }
25362         
25363         
25364         var cfg = {
25365             tag : 'div',
25366             cls : 'btn-group',
25367             cn : [
25368                 {
25369                     tag : 'button',
25370                     cls : 'dropdown-button btn btn-' + this.weight,
25371                     cn : text
25372                 },
25373                 {
25374                     tag : 'button',
25375                     cls : 'dropdown-toggle btn btn-' + this.weight,
25376                     cn : [
25377                         {
25378                             tag : 'span',
25379                             cls : 'caret'
25380                         }
25381                     ]
25382                 },
25383                 {
25384                     tag : 'ul',
25385                     cls : 'dropdown-menu'
25386                 }
25387             ]
25388             
25389         };
25390         
25391         if(this.pos == 'top'){
25392             cfg.cls += ' dropup';
25393         }
25394         
25395         if(this.isSubMenu){
25396             cfg = {
25397                 tag : 'ul',
25398                 cls : 'dropdown-menu'
25399             }
25400         }
25401         
25402         return cfg;
25403     },
25404     
25405     onRender : function(ct, position)
25406     {
25407         this.isSubMenu = ct.hasClass('dropdown-submenu');
25408         
25409         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25410     },
25411     
25412     initEvents : function() 
25413     {
25414         if(this.isSubMenu){
25415             return;
25416         }
25417         
25418         this.hidden = true;
25419         
25420         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25421         this.triggerEl.on('click', this.onTriggerPress, this);
25422         
25423         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25424         this.buttonEl.on('click', this.onClick, this);
25425         
25426     },
25427     
25428     list : function()
25429     {
25430         if(this.isSubMenu){
25431             return this.el;
25432         }
25433         
25434         return this.el.select('ul.dropdown-menu', true).first();
25435     },
25436     
25437     onClick : function(e)
25438     {
25439         this.fireEvent("click", this, e);
25440     },
25441     
25442     onTriggerPress  : function(e)
25443     {   
25444         if (this.isVisible()) {
25445             this.hide();
25446         } else {
25447             this.show();
25448         }
25449     },
25450     
25451     isVisible : function(){
25452         return !this.hidden;
25453     },
25454     
25455     show : function()
25456     {
25457         this.fireEvent("beforeshow", this);
25458         
25459         this.hidden = false;
25460         this.el.addClass('open');
25461         
25462         Roo.get(document).on("mouseup", this.onMouseUp, this);
25463         
25464         this.fireEvent("show", this);
25465         
25466         
25467     },
25468     
25469     hide : function()
25470     {
25471         this.fireEvent("beforehide", this);
25472         
25473         this.hidden = true;
25474         this.el.removeClass('open');
25475         
25476         Roo.get(document).un("mouseup", this.onMouseUp);
25477         
25478         this.fireEvent("hide", this);
25479     },
25480     
25481     onMouseUp : function()
25482     {
25483         this.hide();
25484     }
25485     
25486 });
25487
25488  
25489  /*
25490  * - LGPL
25491  *
25492  * menu item
25493  * 
25494  */
25495 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25496
25497 /**
25498  * @class Roo.bootstrap.menu.Item
25499  * @extends Roo.bootstrap.Component
25500  * Bootstrap MenuItem class
25501  * @cfg {Boolean} submenu (true | false) default false
25502  * @cfg {String} html text of the item
25503  * @cfg {String} href the link
25504  * @cfg {Boolean} disable (true | false) default false
25505  * @cfg {Boolean} preventDefault (true | false) default true
25506  * @cfg {String} icon Font awesome icon
25507  * @cfg {String} pos Submenu align to (left | right) default right 
25508  * 
25509  * 
25510  * @constructor
25511  * Create a new Item
25512  * @param {Object} config The config object
25513  */
25514
25515
25516 Roo.bootstrap.menu.Item = function(config){
25517     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25518     this.addEvents({
25519         /**
25520          * @event mouseover
25521          * Fires when the mouse is hovering over this menu
25522          * @param {Roo.bootstrap.menu.Item} this
25523          * @param {Roo.EventObject} e
25524          */
25525         mouseover : true,
25526         /**
25527          * @event mouseout
25528          * Fires when the mouse exits this menu
25529          * @param {Roo.bootstrap.menu.Item} this
25530          * @param {Roo.EventObject} e
25531          */
25532         mouseout : true,
25533         // raw events
25534         /**
25535          * @event click
25536          * The raw click event for the entire grid.
25537          * @param {Roo.EventObject} e
25538          */
25539         click : true
25540     });
25541 };
25542
25543 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25544     
25545     submenu : false,
25546     href : '',
25547     html : '',
25548     preventDefault: true,
25549     disable : false,
25550     icon : false,
25551     pos : 'right',
25552     
25553     getAutoCreate : function()
25554     {
25555         var text = [
25556             {
25557                 tag : 'span',
25558                 cls : 'roo-menu-item-text',
25559                 html : this.html
25560             }
25561         ];
25562         
25563         if(this.icon){
25564             text.unshift({
25565                 tag : 'i',
25566                 cls : 'fa ' + this.icon
25567             })
25568         }
25569         
25570         var cfg = {
25571             tag : 'li',
25572             cn : [
25573                 {
25574                     tag : 'a',
25575                     href : this.href || '#',
25576                     cn : text
25577                 }
25578             ]
25579         };
25580         
25581         if(this.disable){
25582             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25583         }
25584         
25585         if(this.submenu){
25586             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25587             
25588             if(this.pos == 'left'){
25589                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25590             }
25591         }
25592         
25593         return cfg;
25594     },
25595     
25596     initEvents : function() 
25597     {
25598         this.el.on('mouseover', this.onMouseOver, this);
25599         this.el.on('mouseout', this.onMouseOut, this);
25600         
25601         this.el.select('a', true).first().on('click', this.onClick, this);
25602         
25603     },
25604     
25605     onClick : function(e)
25606     {
25607         if(this.preventDefault){
25608             e.preventDefault();
25609         }
25610         
25611         this.fireEvent("click", this, e);
25612     },
25613     
25614     onMouseOver : function(e)
25615     {
25616         if(this.submenu && this.pos == 'left'){
25617             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25618         }
25619         
25620         this.fireEvent("mouseover", this, e);
25621     },
25622     
25623     onMouseOut : function(e)
25624     {
25625         this.fireEvent("mouseout", this, e);
25626     }
25627 });
25628
25629  
25630
25631  /*
25632  * - LGPL
25633  *
25634  * menu separator
25635  * 
25636  */
25637 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25638
25639 /**
25640  * @class Roo.bootstrap.menu.Separator
25641  * @extends Roo.bootstrap.Component
25642  * Bootstrap Separator class
25643  * 
25644  * @constructor
25645  * Create a new Separator
25646  * @param {Object} config The config object
25647  */
25648
25649
25650 Roo.bootstrap.menu.Separator = function(config){
25651     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25652 };
25653
25654 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25655     
25656     getAutoCreate : function(){
25657         var cfg = {
25658             tag : 'li',
25659             cls: 'divider'
25660         };
25661         
25662         return cfg;
25663     }
25664    
25665 });
25666
25667  
25668
25669  /*
25670  * - LGPL
25671  *
25672  * Tooltip
25673  * 
25674  */
25675
25676 /**
25677  * @class Roo.bootstrap.Tooltip
25678  * Bootstrap Tooltip class
25679  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25680  * to determine which dom element triggers the tooltip.
25681  * 
25682  * It needs to add support for additional attributes like tooltip-position
25683  * 
25684  * @constructor
25685  * Create a new Toolti
25686  * @param {Object} config The config object
25687  */
25688
25689 Roo.bootstrap.Tooltip = function(config){
25690     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25691     
25692     this.alignment = Roo.bootstrap.Tooltip.alignment;
25693     
25694     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25695         this.alignment = config.alignment;
25696     }
25697     
25698 };
25699
25700 Roo.apply(Roo.bootstrap.Tooltip, {
25701     /**
25702      * @function init initialize tooltip monitoring.
25703      * @static
25704      */
25705     currentEl : false,
25706     currentTip : false,
25707     currentRegion : false,
25708     
25709     //  init : delay?
25710     
25711     init : function()
25712     {
25713         Roo.get(document).on('mouseover', this.enter ,this);
25714         Roo.get(document).on('mouseout', this.leave, this);
25715          
25716         
25717         this.currentTip = new Roo.bootstrap.Tooltip();
25718     },
25719     
25720     enter : function(ev)
25721     {
25722         var dom = ev.getTarget();
25723         
25724         //Roo.log(['enter',dom]);
25725         var el = Roo.fly(dom);
25726         if (this.currentEl) {
25727             //Roo.log(dom);
25728             //Roo.log(this.currentEl);
25729             //Roo.log(this.currentEl.contains(dom));
25730             if (this.currentEl == el) {
25731                 return;
25732             }
25733             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25734                 return;
25735             }
25736
25737         }
25738         
25739         if (this.currentTip.el) {
25740             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25741         }    
25742         //Roo.log(ev);
25743         
25744         if(!el || el.dom == document){
25745             return;
25746         }
25747         
25748         var bindEl = el;
25749         
25750         // you can not look for children, as if el is the body.. then everythign is the child..
25751         if (!el.attr('tooltip')) { //
25752             if (!el.select("[tooltip]").elements.length) {
25753                 return;
25754             }
25755             // is the mouse over this child...?
25756             bindEl = el.select("[tooltip]").first();
25757             var xy = ev.getXY();
25758             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25759                 //Roo.log("not in region.");
25760                 return;
25761             }
25762             //Roo.log("child element over..");
25763             
25764         }
25765         this.currentEl = bindEl;
25766         this.currentTip.bind(bindEl);
25767         this.currentRegion = Roo.lib.Region.getRegion(dom);
25768         this.currentTip.enter();
25769         
25770     },
25771     leave : function(ev)
25772     {
25773         var dom = ev.getTarget();
25774         //Roo.log(['leave',dom]);
25775         if (!this.currentEl) {
25776             return;
25777         }
25778         
25779         
25780         if (dom != this.currentEl.dom) {
25781             return;
25782         }
25783         var xy = ev.getXY();
25784         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25785             return;
25786         }
25787         // only activate leave if mouse cursor is outside... bounding box..
25788         
25789         
25790         
25791         
25792         if (this.currentTip) {
25793             this.currentTip.leave();
25794         }
25795         //Roo.log('clear currentEl');
25796         this.currentEl = false;
25797         
25798         
25799     },
25800     alignment : {
25801         'left' : ['r-l', [-2,0], 'right'],
25802         'right' : ['l-r', [2,0], 'left'],
25803         'bottom' : ['t-b', [0,2], 'top'],
25804         'top' : [ 'b-t', [0,-2], 'bottom']
25805     }
25806     
25807 });
25808
25809
25810 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25811     
25812     
25813     bindEl : false,
25814     
25815     delay : null, // can be { show : 300 , hide: 500}
25816     
25817     timeout : null,
25818     
25819     hoverState : null, //???
25820     
25821     placement : 'bottom', 
25822     
25823     alignment : false,
25824     
25825     getAutoCreate : function(){
25826     
25827         var cfg = {
25828            cls : 'tooltip',
25829            role : 'tooltip',
25830            cn : [
25831                 {
25832                     cls : 'tooltip-arrow'
25833                 },
25834                 {
25835                     cls : 'tooltip-inner'
25836                 }
25837            ]
25838         };
25839         
25840         return cfg;
25841     },
25842     bind : function(el)
25843     {
25844         this.bindEl = el;
25845     },
25846       
25847     
25848     enter : function () {
25849        
25850         if (this.timeout != null) {
25851             clearTimeout(this.timeout);
25852         }
25853         
25854         this.hoverState = 'in';
25855          //Roo.log("enter - show");
25856         if (!this.delay || !this.delay.show) {
25857             this.show();
25858             return;
25859         }
25860         var _t = this;
25861         this.timeout = setTimeout(function () {
25862             if (_t.hoverState == 'in') {
25863                 _t.show();
25864             }
25865         }, this.delay.show);
25866     },
25867     leave : function()
25868     {
25869         clearTimeout(this.timeout);
25870     
25871         this.hoverState = 'out';
25872          if (!this.delay || !this.delay.hide) {
25873             this.hide();
25874             return;
25875         }
25876        
25877         var _t = this;
25878         this.timeout = setTimeout(function () {
25879             //Roo.log("leave - timeout");
25880             
25881             if (_t.hoverState == 'out') {
25882                 _t.hide();
25883                 Roo.bootstrap.Tooltip.currentEl = false;
25884             }
25885         }, delay);
25886     },
25887     
25888     show : function (msg)
25889     {
25890         if (!this.el) {
25891             this.render(document.body);
25892         }
25893         // set content.
25894         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25895         
25896         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25897         
25898         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25899         
25900         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25901         
25902         var placement = typeof this.placement == 'function' ?
25903             this.placement.call(this, this.el, on_el) :
25904             this.placement;
25905             
25906         var autoToken = /\s?auto?\s?/i;
25907         var autoPlace = autoToken.test(placement);
25908         if (autoPlace) {
25909             placement = placement.replace(autoToken, '') || 'top';
25910         }
25911         
25912         //this.el.detach()
25913         //this.el.setXY([0,0]);
25914         this.el.show();
25915         //this.el.dom.style.display='block';
25916         
25917         //this.el.appendTo(on_el);
25918         
25919         var p = this.getPosition();
25920         var box = this.el.getBox();
25921         
25922         if (autoPlace) {
25923             // fixme..
25924         }
25925         
25926         var align = this.alignment[placement];
25927         
25928         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25929         
25930         if(placement == 'top' || placement == 'bottom'){
25931             if(xy[0] < 0){
25932                 placement = 'right';
25933             }
25934             
25935             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25936                 placement = 'left';
25937             }
25938             
25939             var scroll = Roo.select('body', true).first().getScroll();
25940             
25941             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25942                 placement = 'top';
25943             }
25944             
25945         }
25946         
25947         this.el.alignTo(this.bindEl, align[0],align[1]);
25948         //var arrow = this.el.select('.arrow',true).first();
25949         //arrow.set(align[2], 
25950         
25951         this.el.addClass(placement);
25952         
25953         this.el.addClass('in fade');
25954         
25955         this.hoverState = null;
25956         
25957         if (this.el.hasClass('fade')) {
25958             // fade it?
25959         }
25960         
25961     },
25962     hide : function()
25963     {
25964          
25965         if (!this.el) {
25966             return;
25967         }
25968         //this.el.setXY([0,0]);
25969         this.el.removeClass('in');
25970         //this.el.hide();
25971         
25972     }
25973     
25974 });
25975  
25976
25977  /*
25978  * - LGPL
25979  *
25980  * Location Picker
25981  * 
25982  */
25983
25984 /**
25985  * @class Roo.bootstrap.LocationPicker
25986  * @extends Roo.bootstrap.Component
25987  * Bootstrap LocationPicker class
25988  * @cfg {Number} latitude Position when init default 0
25989  * @cfg {Number} longitude Position when init default 0
25990  * @cfg {Number} zoom default 15
25991  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25992  * @cfg {Boolean} mapTypeControl default false
25993  * @cfg {Boolean} disableDoubleClickZoom default false
25994  * @cfg {Boolean} scrollwheel default true
25995  * @cfg {Boolean} streetViewControl default false
25996  * @cfg {Number} radius default 0
25997  * @cfg {String} locationName
25998  * @cfg {Boolean} draggable default true
25999  * @cfg {Boolean} enableAutocomplete default false
26000  * @cfg {Boolean} enableReverseGeocode default true
26001  * @cfg {String} markerTitle
26002  * 
26003  * @constructor
26004  * Create a new LocationPicker
26005  * @param {Object} config The config object
26006  */
26007
26008
26009 Roo.bootstrap.LocationPicker = function(config){
26010     
26011     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26012     
26013     this.addEvents({
26014         /**
26015          * @event initial
26016          * Fires when the picker initialized.
26017          * @param {Roo.bootstrap.LocationPicker} this
26018          * @param {Google Location} location
26019          */
26020         initial : true,
26021         /**
26022          * @event positionchanged
26023          * Fires when the picker position changed.
26024          * @param {Roo.bootstrap.LocationPicker} this
26025          * @param {Google Location} location
26026          */
26027         positionchanged : true,
26028         /**
26029          * @event resize
26030          * Fires when the map resize.
26031          * @param {Roo.bootstrap.LocationPicker} this
26032          */
26033         resize : true,
26034         /**
26035          * @event show
26036          * Fires when the map show.
26037          * @param {Roo.bootstrap.LocationPicker} this
26038          */
26039         show : true,
26040         /**
26041          * @event hide
26042          * Fires when the map hide.
26043          * @param {Roo.bootstrap.LocationPicker} this
26044          */
26045         hide : true,
26046         /**
26047          * @event mapClick
26048          * Fires when click the map.
26049          * @param {Roo.bootstrap.LocationPicker} this
26050          * @param {Map event} e
26051          */
26052         mapClick : true,
26053         /**
26054          * @event mapRightClick
26055          * Fires when right click the map.
26056          * @param {Roo.bootstrap.LocationPicker} this
26057          * @param {Map event} e
26058          */
26059         mapRightClick : true,
26060         /**
26061          * @event markerClick
26062          * Fires when click the marker.
26063          * @param {Roo.bootstrap.LocationPicker} this
26064          * @param {Map event} e
26065          */
26066         markerClick : true,
26067         /**
26068          * @event markerRightClick
26069          * Fires when right click the marker.
26070          * @param {Roo.bootstrap.LocationPicker} this
26071          * @param {Map event} e
26072          */
26073         markerRightClick : true,
26074         /**
26075          * @event OverlayViewDraw
26076          * Fires when OverlayView Draw
26077          * @param {Roo.bootstrap.LocationPicker} this
26078          */
26079         OverlayViewDraw : true,
26080         /**
26081          * @event OverlayViewOnAdd
26082          * Fires when OverlayView Draw
26083          * @param {Roo.bootstrap.LocationPicker} this
26084          */
26085         OverlayViewOnAdd : true,
26086         /**
26087          * @event OverlayViewOnRemove
26088          * Fires when OverlayView Draw
26089          * @param {Roo.bootstrap.LocationPicker} this
26090          */
26091         OverlayViewOnRemove : true,
26092         /**
26093          * @event OverlayViewShow
26094          * Fires when OverlayView Draw
26095          * @param {Roo.bootstrap.LocationPicker} this
26096          * @param {Pixel} cpx
26097          */
26098         OverlayViewShow : true,
26099         /**
26100          * @event OverlayViewHide
26101          * Fires when OverlayView Draw
26102          * @param {Roo.bootstrap.LocationPicker} this
26103          */
26104         OverlayViewHide : true,
26105         /**
26106          * @event loadexception
26107          * Fires when load google lib failed.
26108          * @param {Roo.bootstrap.LocationPicker} this
26109          */
26110         loadexception : true
26111     });
26112         
26113 };
26114
26115 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26116     
26117     gMapContext: false,
26118     
26119     latitude: 0,
26120     longitude: 0,
26121     zoom: 15,
26122     mapTypeId: false,
26123     mapTypeControl: false,
26124     disableDoubleClickZoom: false,
26125     scrollwheel: true,
26126     streetViewControl: false,
26127     radius: 0,
26128     locationName: '',
26129     draggable: true,
26130     enableAutocomplete: false,
26131     enableReverseGeocode: true,
26132     markerTitle: '',
26133     
26134     getAutoCreate: function()
26135     {
26136
26137         var cfg = {
26138             tag: 'div',
26139             cls: 'roo-location-picker'
26140         };
26141         
26142         return cfg
26143     },
26144     
26145     initEvents: function(ct, position)
26146     {       
26147         if(!this.el.getWidth() || this.isApplied()){
26148             return;
26149         }
26150         
26151         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26152         
26153         this.initial();
26154     },
26155     
26156     initial: function()
26157     {
26158         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26159             this.fireEvent('loadexception', this);
26160             return;
26161         }
26162         
26163         if(!this.mapTypeId){
26164             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26165         }
26166         
26167         this.gMapContext = this.GMapContext();
26168         
26169         this.initOverlayView();
26170         
26171         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26172         
26173         var _this = this;
26174                 
26175         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26176             _this.setPosition(_this.gMapContext.marker.position);
26177         });
26178         
26179         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26180             _this.fireEvent('mapClick', this, event);
26181             
26182         });
26183
26184         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26185             _this.fireEvent('mapRightClick', this, event);
26186             
26187         });
26188         
26189         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26190             _this.fireEvent('markerClick', this, event);
26191             
26192         });
26193
26194         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26195             _this.fireEvent('markerRightClick', this, event);
26196             
26197         });
26198         
26199         this.setPosition(this.gMapContext.location);
26200         
26201         this.fireEvent('initial', this, this.gMapContext.location);
26202     },
26203     
26204     initOverlayView: function()
26205     {
26206         var _this = this;
26207         
26208         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26209             
26210             draw: function()
26211             {
26212                 _this.fireEvent('OverlayViewDraw', _this);
26213             },
26214             
26215             onAdd: function()
26216             {
26217                 _this.fireEvent('OverlayViewOnAdd', _this);
26218             },
26219             
26220             onRemove: function()
26221             {
26222                 _this.fireEvent('OverlayViewOnRemove', _this);
26223             },
26224             
26225             show: function(cpx)
26226             {
26227                 _this.fireEvent('OverlayViewShow', _this, cpx);
26228             },
26229             
26230             hide: function()
26231             {
26232                 _this.fireEvent('OverlayViewHide', _this);
26233             }
26234             
26235         });
26236     },
26237     
26238     fromLatLngToContainerPixel: function(event)
26239     {
26240         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26241     },
26242     
26243     isApplied: function() 
26244     {
26245         return this.getGmapContext() == false ? false : true;
26246     },
26247     
26248     getGmapContext: function() 
26249     {
26250         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26251     },
26252     
26253     GMapContext: function() 
26254     {
26255         var position = new google.maps.LatLng(this.latitude, this.longitude);
26256         
26257         var _map = new google.maps.Map(this.el.dom, {
26258             center: position,
26259             zoom: this.zoom,
26260             mapTypeId: this.mapTypeId,
26261             mapTypeControl: this.mapTypeControl,
26262             disableDoubleClickZoom: this.disableDoubleClickZoom,
26263             scrollwheel: this.scrollwheel,
26264             streetViewControl: this.streetViewControl,
26265             locationName: this.locationName,
26266             draggable: this.draggable,
26267             enableAutocomplete: this.enableAutocomplete,
26268             enableReverseGeocode: this.enableReverseGeocode
26269         });
26270         
26271         var _marker = new google.maps.Marker({
26272             position: position,
26273             map: _map,
26274             title: this.markerTitle,
26275             draggable: this.draggable
26276         });
26277         
26278         return {
26279             map: _map,
26280             marker: _marker,
26281             circle: null,
26282             location: position,
26283             radius: this.radius,
26284             locationName: this.locationName,
26285             addressComponents: {
26286                 formatted_address: null,
26287                 addressLine1: null,
26288                 addressLine2: null,
26289                 streetName: null,
26290                 streetNumber: null,
26291                 city: null,
26292                 district: null,
26293                 state: null,
26294                 stateOrProvince: null
26295             },
26296             settings: this,
26297             domContainer: this.el.dom,
26298             geodecoder: new google.maps.Geocoder()
26299         };
26300     },
26301     
26302     drawCircle: function(center, radius, options) 
26303     {
26304         if (this.gMapContext.circle != null) {
26305             this.gMapContext.circle.setMap(null);
26306         }
26307         if (radius > 0) {
26308             radius *= 1;
26309             options = Roo.apply({}, options, {
26310                 strokeColor: "#0000FF",
26311                 strokeOpacity: .35,
26312                 strokeWeight: 2,
26313                 fillColor: "#0000FF",
26314                 fillOpacity: .2
26315             });
26316             
26317             options.map = this.gMapContext.map;
26318             options.radius = radius;
26319             options.center = center;
26320             this.gMapContext.circle = new google.maps.Circle(options);
26321             return this.gMapContext.circle;
26322         }
26323         
26324         return null;
26325     },
26326     
26327     setPosition: function(location) 
26328     {
26329         this.gMapContext.location = location;
26330         this.gMapContext.marker.setPosition(location);
26331         this.gMapContext.map.panTo(location);
26332         this.drawCircle(location, this.gMapContext.radius, {});
26333         
26334         var _this = this;
26335         
26336         if (this.gMapContext.settings.enableReverseGeocode) {
26337             this.gMapContext.geodecoder.geocode({
26338                 latLng: this.gMapContext.location
26339             }, function(results, status) {
26340                 
26341                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26342                     _this.gMapContext.locationName = results[0].formatted_address;
26343                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26344                     
26345                     _this.fireEvent('positionchanged', this, location);
26346                 }
26347             });
26348             
26349             return;
26350         }
26351         
26352         this.fireEvent('positionchanged', this, location);
26353     },
26354     
26355     resize: function()
26356     {
26357         google.maps.event.trigger(this.gMapContext.map, "resize");
26358         
26359         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26360         
26361         this.fireEvent('resize', this);
26362     },
26363     
26364     setPositionByLatLng: function(latitude, longitude)
26365     {
26366         this.setPosition(new google.maps.LatLng(latitude, longitude));
26367     },
26368     
26369     getCurrentPosition: function() 
26370     {
26371         return {
26372             latitude: this.gMapContext.location.lat(),
26373             longitude: this.gMapContext.location.lng()
26374         };
26375     },
26376     
26377     getAddressName: function() 
26378     {
26379         return this.gMapContext.locationName;
26380     },
26381     
26382     getAddressComponents: function() 
26383     {
26384         return this.gMapContext.addressComponents;
26385     },
26386     
26387     address_component_from_google_geocode: function(address_components) 
26388     {
26389         var result = {};
26390         
26391         for (var i = 0; i < address_components.length; i++) {
26392             var component = address_components[i];
26393             if (component.types.indexOf("postal_code") >= 0) {
26394                 result.postalCode = component.short_name;
26395             } else if (component.types.indexOf("street_number") >= 0) {
26396                 result.streetNumber = component.short_name;
26397             } else if (component.types.indexOf("route") >= 0) {
26398                 result.streetName = component.short_name;
26399             } else if (component.types.indexOf("neighborhood") >= 0) {
26400                 result.city = component.short_name;
26401             } else if (component.types.indexOf("locality") >= 0) {
26402                 result.city = component.short_name;
26403             } else if (component.types.indexOf("sublocality") >= 0) {
26404                 result.district = component.short_name;
26405             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26406                 result.stateOrProvince = component.short_name;
26407             } else if (component.types.indexOf("country") >= 0) {
26408                 result.country = component.short_name;
26409             }
26410         }
26411         
26412         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26413         result.addressLine2 = "";
26414         return result;
26415     },
26416     
26417     setZoomLevel: function(zoom)
26418     {
26419         this.gMapContext.map.setZoom(zoom);
26420     },
26421     
26422     show: function()
26423     {
26424         if(!this.el){
26425             return;
26426         }
26427         
26428         this.el.show();
26429         
26430         this.resize();
26431         
26432         this.fireEvent('show', this);
26433     },
26434     
26435     hide: function()
26436     {
26437         if(!this.el){
26438             return;
26439         }
26440         
26441         this.el.hide();
26442         
26443         this.fireEvent('hide', this);
26444     }
26445     
26446 });
26447
26448 Roo.apply(Roo.bootstrap.LocationPicker, {
26449     
26450     OverlayView : function(map, options)
26451     {
26452         options = options || {};
26453         
26454         this.setMap(map);
26455     }
26456     
26457     
26458 });/*
26459  * - LGPL
26460  *
26461  * Alert
26462  * 
26463  */
26464
26465 /**
26466  * @class Roo.bootstrap.Alert
26467  * @extends Roo.bootstrap.Component
26468  * Bootstrap Alert class
26469  * @cfg {String} title The title of alert
26470  * @cfg {String} html The content of alert
26471  * @cfg {String} weight (  success | info | warning | danger )
26472  * @cfg {String} faicon font-awesomeicon
26473  * 
26474  * @constructor
26475  * Create a new alert
26476  * @param {Object} config The config object
26477  */
26478
26479
26480 Roo.bootstrap.Alert = function(config){
26481     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26482     
26483 };
26484
26485 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26486     
26487     title: '',
26488     html: '',
26489     weight: false,
26490     faicon: false,
26491     
26492     getAutoCreate : function()
26493     {
26494         
26495         var cfg = {
26496             tag : 'div',
26497             cls : 'alert',
26498             cn : [
26499                 {
26500                     tag : 'i',
26501                     cls : 'roo-alert-icon'
26502                     
26503                 },
26504                 {
26505                     tag : 'b',
26506                     cls : 'roo-alert-title',
26507                     html : this.title
26508                 },
26509                 {
26510                     tag : 'span',
26511                     cls : 'roo-alert-text',
26512                     html : this.html
26513                 }
26514             ]
26515         };
26516         
26517         if(this.faicon){
26518             cfg.cn[0].cls += ' fa ' + this.faicon;
26519         }
26520         
26521         if(this.weight){
26522             cfg.cls += ' alert-' + this.weight;
26523         }
26524         
26525         return cfg;
26526     },
26527     
26528     initEvents: function() 
26529     {
26530         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26531     },
26532     
26533     setTitle : function(str)
26534     {
26535         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26536     },
26537     
26538     setText : function(str)
26539     {
26540         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26541     },
26542     
26543     setWeight : function(weight)
26544     {
26545         if(this.weight){
26546             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26547         }
26548         
26549         this.weight = weight;
26550         
26551         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26552     },
26553     
26554     setIcon : function(icon)
26555     {
26556         if(this.faicon){
26557             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26558         }
26559         
26560         this.faicon = icon;
26561         
26562         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26563     },
26564     
26565     hide: function() 
26566     {
26567         this.el.hide();   
26568     },
26569     
26570     show: function() 
26571     {  
26572         this.el.show();   
26573     }
26574     
26575 });
26576
26577  
26578 /*
26579 * Licence: LGPL
26580 */
26581
26582 /**
26583  * @class Roo.bootstrap.UploadCropbox
26584  * @extends Roo.bootstrap.Component
26585  * Bootstrap UploadCropbox class
26586  * @cfg {String} emptyText show when image has been loaded
26587  * @cfg {String} rotateNotify show when image too small to rotate
26588  * @cfg {Number} errorTimeout default 3000
26589  * @cfg {Number} minWidth default 300
26590  * @cfg {Number} minHeight default 300
26591  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26592  * @cfg {Boolean} isDocument (true|false) default false
26593  * @cfg {String} url action url
26594  * @cfg {String} paramName default 'imageUpload'
26595  * @cfg {String} method default POST
26596  * @cfg {Boolean} loadMask (true|false) default true
26597  * @cfg {Boolean} loadingText default 'Loading...'
26598  * 
26599  * @constructor
26600  * Create a new UploadCropbox
26601  * @param {Object} config The config object
26602  */
26603
26604 Roo.bootstrap.UploadCropbox = function(config){
26605     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26606     
26607     this.addEvents({
26608         /**
26609          * @event beforeselectfile
26610          * Fire before select file
26611          * @param {Roo.bootstrap.UploadCropbox} this
26612          */
26613         "beforeselectfile" : true,
26614         /**
26615          * @event initial
26616          * Fire after initEvent
26617          * @param {Roo.bootstrap.UploadCropbox} this
26618          */
26619         "initial" : true,
26620         /**
26621          * @event crop
26622          * Fire after initEvent
26623          * @param {Roo.bootstrap.UploadCropbox} this
26624          * @param {String} data
26625          */
26626         "crop" : true,
26627         /**
26628          * @event prepare
26629          * Fire when preparing the file data
26630          * @param {Roo.bootstrap.UploadCropbox} this
26631          * @param {Object} file
26632          */
26633         "prepare" : true,
26634         /**
26635          * @event exception
26636          * Fire when get exception
26637          * @param {Roo.bootstrap.UploadCropbox} this
26638          * @param {XMLHttpRequest} xhr
26639          */
26640         "exception" : true,
26641         /**
26642          * @event beforeloadcanvas
26643          * Fire before load the canvas
26644          * @param {Roo.bootstrap.UploadCropbox} this
26645          * @param {String} src
26646          */
26647         "beforeloadcanvas" : true,
26648         /**
26649          * @event trash
26650          * Fire when trash image
26651          * @param {Roo.bootstrap.UploadCropbox} this
26652          */
26653         "trash" : true,
26654         /**
26655          * @event download
26656          * Fire when download the image
26657          * @param {Roo.bootstrap.UploadCropbox} this
26658          */
26659         "download" : true,
26660         /**
26661          * @event footerbuttonclick
26662          * Fire when footerbuttonclick
26663          * @param {Roo.bootstrap.UploadCropbox} this
26664          * @param {String} type
26665          */
26666         "footerbuttonclick" : true,
26667         /**
26668          * @event resize
26669          * Fire when resize
26670          * @param {Roo.bootstrap.UploadCropbox} this
26671          */
26672         "resize" : true,
26673         /**
26674          * @event rotate
26675          * Fire when rotate the image
26676          * @param {Roo.bootstrap.UploadCropbox} this
26677          * @param {String} pos
26678          */
26679         "rotate" : true,
26680         /**
26681          * @event inspect
26682          * Fire when inspect the file
26683          * @param {Roo.bootstrap.UploadCropbox} this
26684          * @param {Object} file
26685          */
26686         "inspect" : true,
26687         /**
26688          * @event upload
26689          * Fire when xhr upload the file
26690          * @param {Roo.bootstrap.UploadCropbox} this
26691          * @param {Object} data
26692          */
26693         "upload" : true,
26694         /**
26695          * @event arrange
26696          * Fire when arrange the file data
26697          * @param {Roo.bootstrap.UploadCropbox} this
26698          * @param {Object} formData
26699          */
26700         "arrange" : true
26701     });
26702     
26703     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26704 };
26705
26706 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26707     
26708     emptyText : 'Click to upload image',
26709     rotateNotify : 'Image is too small to rotate',
26710     errorTimeout : 3000,
26711     scale : 0,
26712     baseScale : 1,
26713     rotate : 0,
26714     dragable : false,
26715     pinching : false,
26716     mouseX : 0,
26717     mouseY : 0,
26718     cropData : false,
26719     minWidth : 300,
26720     minHeight : 300,
26721     file : false,
26722     exif : {},
26723     baseRotate : 1,
26724     cropType : 'image/jpeg',
26725     buttons : false,
26726     canvasLoaded : false,
26727     isDocument : false,
26728     method : 'POST',
26729     paramName : 'imageUpload',
26730     loadMask : true,
26731     loadingText : 'Loading...',
26732     maskEl : false,
26733     
26734     getAutoCreate : function()
26735     {
26736         var cfg = {
26737             tag : 'div',
26738             cls : 'roo-upload-cropbox',
26739             cn : [
26740                 {
26741                     tag : 'input',
26742                     cls : 'roo-upload-cropbox-selector',
26743                     type : 'file'
26744                 },
26745                 {
26746                     tag : 'div',
26747                     cls : 'roo-upload-cropbox-body',
26748                     style : 'cursor:pointer',
26749                     cn : [
26750                         {
26751                             tag : 'div',
26752                             cls : 'roo-upload-cropbox-preview'
26753                         },
26754                         {
26755                             tag : 'div',
26756                             cls : 'roo-upload-cropbox-thumb'
26757                         },
26758                         {
26759                             tag : 'div',
26760                             cls : 'roo-upload-cropbox-empty-notify',
26761                             html : this.emptyText
26762                         },
26763                         {
26764                             tag : 'div',
26765                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26766                             html : this.rotateNotify
26767                         }
26768                     ]
26769                 },
26770                 {
26771                     tag : 'div',
26772                     cls : 'roo-upload-cropbox-footer',
26773                     cn : {
26774                         tag : 'div',
26775                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26776                         cn : []
26777                     }
26778                 }
26779             ]
26780         };
26781         
26782         return cfg;
26783     },
26784     
26785     onRender : function(ct, position)
26786     {
26787         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26788         
26789         if (this.buttons.length) {
26790             
26791             Roo.each(this.buttons, function(bb) {
26792                 
26793                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26794                 
26795                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26796                 
26797             }, this);
26798         }
26799         
26800         if(this.loadMask){
26801             this.maskEl = this.el;
26802         }
26803     },
26804     
26805     initEvents : function()
26806     {
26807         this.urlAPI = (window.createObjectURL && window) || 
26808                                 (window.URL && URL.revokeObjectURL && URL) || 
26809                                 (window.webkitURL && webkitURL);
26810                         
26811         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26812         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26813         
26814         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26815         this.selectorEl.hide();
26816         
26817         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26818         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26819         
26820         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26821         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26822         this.thumbEl.hide();
26823         
26824         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26825         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26826         
26827         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26828         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26829         this.errorEl.hide();
26830         
26831         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26832         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26833         this.footerEl.hide();
26834         
26835         this.setThumbBoxSize();
26836         
26837         this.bind();
26838         
26839         this.resize();
26840         
26841         this.fireEvent('initial', this);
26842     },
26843
26844     bind : function()
26845     {
26846         var _this = this;
26847         
26848         window.addEventListener("resize", function() { _this.resize(); } );
26849         
26850         this.bodyEl.on('click', this.beforeSelectFile, this);
26851         
26852         if(Roo.isTouch){
26853             this.bodyEl.on('touchstart', this.onTouchStart, this);
26854             this.bodyEl.on('touchmove', this.onTouchMove, this);
26855             this.bodyEl.on('touchend', this.onTouchEnd, this);
26856         }
26857         
26858         if(!Roo.isTouch){
26859             this.bodyEl.on('mousedown', this.onMouseDown, this);
26860             this.bodyEl.on('mousemove', this.onMouseMove, this);
26861             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26862             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26863             Roo.get(document).on('mouseup', this.onMouseUp, this);
26864         }
26865         
26866         this.selectorEl.on('change', this.onFileSelected, this);
26867     },
26868     
26869     reset : function()
26870     {    
26871         this.scale = 0;
26872         this.baseScale = 1;
26873         this.rotate = 0;
26874         this.baseRotate = 1;
26875         this.dragable = false;
26876         this.pinching = false;
26877         this.mouseX = 0;
26878         this.mouseY = 0;
26879         this.cropData = false;
26880         this.notifyEl.dom.innerHTML = this.emptyText;
26881         
26882         this.selectorEl.dom.value = '';
26883         
26884     },
26885     
26886     resize : function()
26887     {
26888         if(this.fireEvent('resize', this) != false){
26889             this.setThumbBoxPosition();
26890             this.setCanvasPosition();
26891         }
26892     },
26893     
26894     onFooterButtonClick : function(e, el, o, type)
26895     {
26896         switch (type) {
26897             case 'rotate-left' :
26898                 this.onRotateLeft(e);
26899                 break;
26900             case 'rotate-right' :
26901                 this.onRotateRight(e);
26902                 break;
26903             case 'picture' :
26904                 this.beforeSelectFile(e);
26905                 break;
26906             case 'trash' :
26907                 this.trash(e);
26908                 break;
26909             case 'crop' :
26910                 this.crop(e);
26911                 break;
26912             case 'download' :
26913                 this.download(e);
26914                 break;
26915             default :
26916                 break;
26917         }
26918         
26919         this.fireEvent('footerbuttonclick', this, type);
26920     },
26921     
26922     beforeSelectFile : function(e)
26923     {
26924         e.preventDefault();
26925         
26926         if(this.fireEvent('beforeselectfile', this) != false){
26927             this.selectorEl.dom.click();
26928         }
26929     },
26930     
26931     onFileSelected : function(e)
26932     {
26933         e.preventDefault();
26934         
26935         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26936             return;
26937         }
26938         
26939         var file = this.selectorEl.dom.files[0];
26940         
26941         if(this.fireEvent('inspect', this, file) != false){
26942             this.prepare(file);
26943         }
26944         
26945     },
26946     
26947     trash : function(e)
26948     {
26949         this.fireEvent('trash', this);
26950     },
26951     
26952     download : function(e)
26953     {
26954         this.fireEvent('download', this);
26955     },
26956     
26957     loadCanvas : function(src)
26958     {   
26959         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26960             
26961             this.reset();
26962             
26963             this.imageEl = document.createElement('img');
26964             
26965             var _this = this;
26966             
26967             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26968             
26969             this.imageEl.src = src;
26970         }
26971     },
26972     
26973     onLoadCanvas : function()
26974     {   
26975         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26976         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26977         
26978         this.bodyEl.un('click', this.beforeSelectFile, this);
26979         
26980         this.notifyEl.hide();
26981         this.thumbEl.show();
26982         this.footerEl.show();
26983         
26984         this.baseRotateLevel();
26985         
26986         if(this.isDocument){
26987             this.setThumbBoxSize();
26988         }
26989         
26990         this.setThumbBoxPosition();
26991         
26992         this.baseScaleLevel();
26993         
26994         this.draw();
26995         
26996         this.resize();
26997         
26998         this.canvasLoaded = true;
26999         
27000         if(this.loadMask){
27001             this.maskEl.unmask();
27002         }
27003         
27004     },
27005     
27006     setCanvasPosition : function()
27007     {   
27008         if(!this.canvasEl){
27009             return;
27010         }
27011         
27012         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27013         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27014         
27015         this.previewEl.setLeft(pw);
27016         this.previewEl.setTop(ph);
27017         
27018     },
27019     
27020     onMouseDown : function(e)
27021     {   
27022         e.stopEvent();
27023         
27024         this.dragable = true;
27025         this.pinching = false;
27026         
27027         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27028             this.dragable = false;
27029             return;
27030         }
27031         
27032         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27033         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27034         
27035     },
27036     
27037     onMouseMove : function(e)
27038     {   
27039         e.stopEvent();
27040         
27041         if(!this.canvasLoaded){
27042             return;
27043         }
27044         
27045         if (!this.dragable){
27046             return;
27047         }
27048         
27049         var minX = Math.ceil(this.thumbEl.getLeft(true));
27050         var minY = Math.ceil(this.thumbEl.getTop(true));
27051         
27052         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27053         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27054         
27055         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27056         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27057         
27058         x = x - this.mouseX;
27059         y = y - this.mouseY;
27060         
27061         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27062         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27063         
27064         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27065         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27066         
27067         this.previewEl.setLeft(bgX);
27068         this.previewEl.setTop(bgY);
27069         
27070         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27071         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27072     },
27073     
27074     onMouseUp : function(e)
27075     {   
27076         e.stopEvent();
27077         
27078         this.dragable = false;
27079     },
27080     
27081     onMouseWheel : function(e)
27082     {   
27083         e.stopEvent();
27084         
27085         this.startScale = this.scale;
27086         
27087         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27088         
27089         if(!this.zoomable()){
27090             this.scale = this.startScale;
27091             return;
27092         }
27093         
27094         this.draw();
27095         
27096         return;
27097     },
27098     
27099     zoomable : function()
27100     {
27101         var minScale = this.thumbEl.getWidth() / this.minWidth;
27102         
27103         if(this.minWidth < this.minHeight){
27104             minScale = this.thumbEl.getHeight() / this.minHeight;
27105         }
27106         
27107         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27108         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27109         
27110         if(
27111                 this.isDocument &&
27112                 (this.rotate == 0 || this.rotate == 180) && 
27113                 (
27114                     width > this.imageEl.OriginWidth || 
27115                     height > this.imageEl.OriginHeight ||
27116                     (width < this.minWidth && height < this.minHeight)
27117                 )
27118         ){
27119             return false;
27120         }
27121         
27122         if(
27123                 this.isDocument &&
27124                 (this.rotate == 90 || this.rotate == 270) && 
27125                 (
27126                     width > this.imageEl.OriginWidth || 
27127                     height > this.imageEl.OriginHeight ||
27128                     (width < this.minHeight && height < this.minWidth)
27129                 )
27130         ){
27131             return false;
27132         }
27133         
27134         if(
27135                 !this.isDocument &&
27136                 (this.rotate == 0 || this.rotate == 180) && 
27137                 (
27138                     width < this.minWidth || 
27139                     width > this.imageEl.OriginWidth || 
27140                     height < this.minHeight || 
27141                     height > this.imageEl.OriginHeight
27142                 )
27143         ){
27144             return false;
27145         }
27146         
27147         if(
27148                 !this.isDocument &&
27149                 (this.rotate == 90 || this.rotate == 270) && 
27150                 (
27151                     width < this.minHeight || 
27152                     width > this.imageEl.OriginWidth || 
27153                     height < this.minWidth || 
27154                     height > this.imageEl.OriginHeight
27155                 )
27156         ){
27157             return false;
27158         }
27159         
27160         return true;
27161         
27162     },
27163     
27164     onRotateLeft : function(e)
27165     {   
27166         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27167             
27168             var minScale = this.thumbEl.getWidth() / this.minWidth;
27169             
27170             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27171             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27172             
27173             this.startScale = this.scale;
27174             
27175             while (this.getScaleLevel() < minScale){
27176             
27177                 this.scale = this.scale + 1;
27178                 
27179                 if(!this.zoomable()){
27180                     break;
27181                 }
27182                 
27183                 if(
27184                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27185                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27186                 ){
27187                     continue;
27188                 }
27189                 
27190                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27191
27192                 this.draw();
27193                 
27194                 return;
27195             }
27196             
27197             this.scale = this.startScale;
27198             
27199             this.onRotateFail();
27200             
27201             return false;
27202         }
27203         
27204         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27205
27206         if(this.isDocument){
27207             this.setThumbBoxSize();
27208             this.setThumbBoxPosition();
27209             this.setCanvasPosition();
27210         }
27211         
27212         this.draw();
27213         
27214         this.fireEvent('rotate', this, 'left');
27215         
27216     },
27217     
27218     onRotateRight : function(e)
27219     {
27220         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27221             
27222             var minScale = this.thumbEl.getWidth() / this.minWidth;
27223         
27224             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27225             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27226             
27227             this.startScale = this.scale;
27228             
27229             while (this.getScaleLevel() < minScale){
27230             
27231                 this.scale = this.scale + 1;
27232                 
27233                 if(!this.zoomable()){
27234                     break;
27235                 }
27236                 
27237                 if(
27238                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27239                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27240                 ){
27241                     continue;
27242                 }
27243                 
27244                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27245
27246                 this.draw();
27247                 
27248                 return;
27249             }
27250             
27251             this.scale = this.startScale;
27252             
27253             this.onRotateFail();
27254             
27255             return false;
27256         }
27257         
27258         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27259
27260         if(this.isDocument){
27261             this.setThumbBoxSize();
27262             this.setThumbBoxPosition();
27263             this.setCanvasPosition();
27264         }
27265         
27266         this.draw();
27267         
27268         this.fireEvent('rotate', this, 'right');
27269     },
27270     
27271     onRotateFail : function()
27272     {
27273         this.errorEl.show(true);
27274         
27275         var _this = this;
27276         
27277         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27278     },
27279     
27280     draw : function()
27281     {
27282         this.previewEl.dom.innerHTML = '';
27283         
27284         var canvasEl = document.createElement("canvas");
27285         
27286         var contextEl = canvasEl.getContext("2d");
27287         
27288         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27289         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27290         var center = this.imageEl.OriginWidth / 2;
27291         
27292         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27293             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27294             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27295             center = this.imageEl.OriginHeight / 2;
27296         }
27297         
27298         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27299         
27300         contextEl.translate(center, center);
27301         contextEl.rotate(this.rotate * Math.PI / 180);
27302
27303         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27304         
27305         this.canvasEl = document.createElement("canvas");
27306         
27307         this.contextEl = this.canvasEl.getContext("2d");
27308         
27309         switch (this.rotate) {
27310             case 0 :
27311                 
27312                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27313                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27314                 
27315                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27316                 
27317                 break;
27318             case 90 : 
27319                 
27320                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27321                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27322                 
27323                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27324                     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);
27325                     break;
27326                 }
27327                 
27328                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27329                 
27330                 break;
27331             case 180 :
27332                 
27333                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27334                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27335                 
27336                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27337                     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);
27338                     break;
27339                 }
27340                 
27341                 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);
27342                 
27343                 break;
27344             case 270 :
27345                 
27346                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27347                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27348         
27349                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27350                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27351                     break;
27352                 }
27353                 
27354                 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);
27355                 
27356                 break;
27357             default : 
27358                 break;
27359         }
27360         
27361         this.previewEl.appendChild(this.canvasEl);
27362         
27363         this.setCanvasPosition();
27364     },
27365     
27366     crop : function()
27367     {
27368         if(!this.canvasLoaded){
27369             return;
27370         }
27371         
27372         var imageCanvas = document.createElement("canvas");
27373         
27374         var imageContext = imageCanvas.getContext("2d");
27375         
27376         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27377         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27378         
27379         var center = imageCanvas.width / 2;
27380         
27381         imageContext.translate(center, center);
27382         
27383         imageContext.rotate(this.rotate * Math.PI / 180);
27384         
27385         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27386         
27387         var canvas = document.createElement("canvas");
27388         
27389         var context = canvas.getContext("2d");
27390                 
27391         canvas.width = this.minWidth;
27392         canvas.height = this.minHeight;
27393
27394         switch (this.rotate) {
27395             case 0 :
27396                 
27397                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27398                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27399                 
27400                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27401                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27402                 
27403                 var targetWidth = this.minWidth - 2 * x;
27404                 var targetHeight = this.minHeight - 2 * y;
27405                 
27406                 var scale = 1;
27407                 
27408                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27409                     scale = targetWidth / width;
27410                 }
27411                 
27412                 if(x > 0 && y == 0){
27413                     scale = targetHeight / height;
27414                 }
27415                 
27416                 if(x > 0 && y > 0){
27417                     scale = targetWidth / width;
27418                     
27419                     if(width < height){
27420                         scale = targetHeight / height;
27421                     }
27422                 }
27423                 
27424                 context.scale(scale, scale);
27425                 
27426                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27427                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27428
27429                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27430                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27431
27432                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27433                 
27434                 break;
27435             case 90 : 
27436                 
27437                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27438                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27439                 
27440                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27441                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27442                 
27443                 var targetWidth = this.minWidth - 2 * x;
27444                 var targetHeight = this.minHeight - 2 * y;
27445                 
27446                 var scale = 1;
27447                 
27448                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27449                     scale = targetWidth / width;
27450                 }
27451                 
27452                 if(x > 0 && y == 0){
27453                     scale = targetHeight / height;
27454                 }
27455                 
27456                 if(x > 0 && y > 0){
27457                     scale = targetWidth / width;
27458                     
27459                     if(width < height){
27460                         scale = targetHeight / height;
27461                     }
27462                 }
27463                 
27464                 context.scale(scale, scale);
27465                 
27466                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27467                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27468
27469                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27470                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27471                 
27472                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27473                 
27474                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27475                 
27476                 break;
27477             case 180 :
27478                 
27479                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27480                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27481                 
27482                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27483                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27484                 
27485                 var targetWidth = this.minWidth - 2 * x;
27486                 var targetHeight = this.minHeight - 2 * y;
27487                 
27488                 var scale = 1;
27489                 
27490                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27491                     scale = targetWidth / width;
27492                 }
27493                 
27494                 if(x > 0 && y == 0){
27495                     scale = targetHeight / height;
27496                 }
27497                 
27498                 if(x > 0 && y > 0){
27499                     scale = targetWidth / width;
27500                     
27501                     if(width < height){
27502                         scale = targetHeight / height;
27503                     }
27504                 }
27505                 
27506                 context.scale(scale, scale);
27507                 
27508                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27509                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27510
27511                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27512                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27513
27514                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27515                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27516                 
27517                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27518                 
27519                 break;
27520             case 270 :
27521                 
27522                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27523                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27524                 
27525                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27526                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27527                 
27528                 var targetWidth = this.minWidth - 2 * x;
27529                 var targetHeight = this.minHeight - 2 * y;
27530                 
27531                 var scale = 1;
27532                 
27533                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27534                     scale = targetWidth / width;
27535                 }
27536                 
27537                 if(x > 0 && y == 0){
27538                     scale = targetHeight / height;
27539                 }
27540                 
27541                 if(x > 0 && y > 0){
27542                     scale = targetWidth / width;
27543                     
27544                     if(width < height){
27545                         scale = targetHeight / height;
27546                     }
27547                 }
27548                 
27549                 context.scale(scale, scale);
27550                 
27551                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27552                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27553
27554                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27555                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27556                 
27557                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27558                 
27559                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27560                 
27561                 break;
27562             default : 
27563                 break;
27564         }
27565         
27566         this.cropData = canvas.toDataURL(this.cropType);
27567         
27568         if(this.fireEvent('crop', this, this.cropData) !== false){
27569             this.process(this.file, this.cropData);
27570         }
27571         
27572         return;
27573         
27574     },
27575     
27576     setThumbBoxSize : function()
27577     {
27578         var width, height;
27579         
27580         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27581             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27582             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27583             
27584             this.minWidth = width;
27585             this.minHeight = height;
27586             
27587             if(this.rotate == 90 || this.rotate == 270){
27588                 this.minWidth = height;
27589                 this.minHeight = width;
27590             }
27591         }
27592         
27593         height = 300;
27594         width = Math.ceil(this.minWidth * height / this.minHeight);
27595         
27596         if(this.minWidth > this.minHeight){
27597             width = 300;
27598             height = Math.ceil(this.minHeight * width / this.minWidth);
27599         }
27600         
27601         this.thumbEl.setStyle({
27602             width : width + 'px',
27603             height : height + 'px'
27604         });
27605
27606         return;
27607             
27608     },
27609     
27610     setThumbBoxPosition : function()
27611     {
27612         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27613         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27614         
27615         this.thumbEl.setLeft(x);
27616         this.thumbEl.setTop(y);
27617         
27618     },
27619     
27620     baseRotateLevel : function()
27621     {
27622         this.baseRotate = 1;
27623         
27624         if(
27625                 typeof(this.exif) != 'undefined' &&
27626                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27627                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27628         ){
27629             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27630         }
27631         
27632         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27633         
27634     },
27635     
27636     baseScaleLevel : function()
27637     {
27638         var width, height;
27639         
27640         if(this.isDocument){
27641             
27642             if(this.baseRotate == 6 || this.baseRotate == 8){
27643             
27644                 height = this.thumbEl.getHeight();
27645                 this.baseScale = height / this.imageEl.OriginWidth;
27646
27647                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27648                     width = this.thumbEl.getWidth();
27649                     this.baseScale = width / this.imageEl.OriginHeight;
27650                 }
27651
27652                 return;
27653             }
27654
27655             height = this.thumbEl.getHeight();
27656             this.baseScale = height / this.imageEl.OriginHeight;
27657
27658             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27659                 width = this.thumbEl.getWidth();
27660                 this.baseScale = width / this.imageEl.OriginWidth;
27661             }
27662
27663             return;
27664         }
27665         
27666         if(this.baseRotate == 6 || this.baseRotate == 8){
27667             
27668             width = this.thumbEl.getHeight();
27669             this.baseScale = width / this.imageEl.OriginHeight;
27670             
27671             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27672                 height = this.thumbEl.getWidth();
27673                 this.baseScale = height / this.imageEl.OriginHeight;
27674             }
27675             
27676             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27677                 height = this.thumbEl.getWidth();
27678                 this.baseScale = height / this.imageEl.OriginHeight;
27679                 
27680                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27681                     width = this.thumbEl.getHeight();
27682                     this.baseScale = width / this.imageEl.OriginWidth;
27683                 }
27684             }
27685             
27686             return;
27687         }
27688         
27689         width = this.thumbEl.getWidth();
27690         this.baseScale = width / this.imageEl.OriginWidth;
27691         
27692         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27693             height = this.thumbEl.getHeight();
27694             this.baseScale = height / this.imageEl.OriginHeight;
27695         }
27696         
27697         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27698             
27699             height = this.thumbEl.getHeight();
27700             this.baseScale = height / this.imageEl.OriginHeight;
27701             
27702             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27703                 width = this.thumbEl.getWidth();
27704                 this.baseScale = width / this.imageEl.OriginWidth;
27705             }
27706             
27707         }
27708         
27709         return;
27710     },
27711     
27712     getScaleLevel : function()
27713     {
27714         return this.baseScale * Math.pow(1.1, this.scale);
27715     },
27716     
27717     onTouchStart : function(e)
27718     {
27719         if(!this.canvasLoaded){
27720             this.beforeSelectFile(e);
27721             return;
27722         }
27723         
27724         var touches = e.browserEvent.touches;
27725         
27726         if(!touches){
27727             return;
27728         }
27729         
27730         if(touches.length == 1){
27731             this.onMouseDown(e);
27732             return;
27733         }
27734         
27735         if(touches.length != 2){
27736             return;
27737         }
27738         
27739         var coords = [];
27740         
27741         for(var i = 0, finger; finger = touches[i]; i++){
27742             coords.push(finger.pageX, finger.pageY);
27743         }
27744         
27745         var x = Math.pow(coords[0] - coords[2], 2);
27746         var y = Math.pow(coords[1] - coords[3], 2);
27747         
27748         this.startDistance = Math.sqrt(x + y);
27749         
27750         this.startScale = this.scale;
27751         
27752         this.pinching = true;
27753         this.dragable = false;
27754         
27755     },
27756     
27757     onTouchMove : function(e)
27758     {
27759         if(!this.pinching && !this.dragable){
27760             return;
27761         }
27762         
27763         var touches = e.browserEvent.touches;
27764         
27765         if(!touches){
27766             return;
27767         }
27768         
27769         if(this.dragable){
27770             this.onMouseMove(e);
27771             return;
27772         }
27773         
27774         var coords = [];
27775         
27776         for(var i = 0, finger; finger = touches[i]; i++){
27777             coords.push(finger.pageX, finger.pageY);
27778         }
27779         
27780         var x = Math.pow(coords[0] - coords[2], 2);
27781         var y = Math.pow(coords[1] - coords[3], 2);
27782         
27783         this.endDistance = Math.sqrt(x + y);
27784         
27785         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27786         
27787         if(!this.zoomable()){
27788             this.scale = this.startScale;
27789             return;
27790         }
27791         
27792         this.draw();
27793         
27794     },
27795     
27796     onTouchEnd : function(e)
27797     {
27798         this.pinching = false;
27799         this.dragable = false;
27800         
27801     },
27802     
27803     process : function(file, crop)
27804     {
27805         if(this.loadMask){
27806             this.maskEl.mask(this.loadingText);
27807         }
27808         
27809         this.xhr = new XMLHttpRequest();
27810         
27811         file.xhr = this.xhr;
27812
27813         this.xhr.open(this.method, this.url, true);
27814         
27815         var headers = {
27816             "Accept": "application/json",
27817             "Cache-Control": "no-cache",
27818             "X-Requested-With": "XMLHttpRequest"
27819         };
27820         
27821         for (var headerName in headers) {
27822             var headerValue = headers[headerName];
27823             if (headerValue) {
27824                 this.xhr.setRequestHeader(headerName, headerValue);
27825             }
27826         }
27827         
27828         var _this = this;
27829         
27830         this.xhr.onload = function()
27831         {
27832             _this.xhrOnLoad(_this.xhr);
27833         }
27834         
27835         this.xhr.onerror = function()
27836         {
27837             _this.xhrOnError(_this.xhr);
27838         }
27839         
27840         var formData = new FormData();
27841
27842         formData.append('returnHTML', 'NO');
27843         
27844         if(crop){
27845             formData.append('crop', crop);
27846         }
27847         
27848         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27849             formData.append(this.paramName, file, file.name);
27850         }
27851         
27852         if(typeof(file.filename) != 'undefined'){
27853             formData.append('filename', file.filename);
27854         }
27855         
27856         if(typeof(file.mimetype) != 'undefined'){
27857             formData.append('mimetype', file.mimetype);
27858         }
27859         
27860         if(this.fireEvent('arrange', this, formData) != false){
27861             this.xhr.send(formData);
27862         };
27863     },
27864     
27865     xhrOnLoad : function(xhr)
27866     {
27867         if(this.loadMask){
27868             this.maskEl.unmask();
27869         }
27870         
27871         if (xhr.readyState !== 4) {
27872             this.fireEvent('exception', this, xhr);
27873             return;
27874         }
27875
27876         var response = Roo.decode(xhr.responseText);
27877         
27878         if(!response.success){
27879             this.fireEvent('exception', this, xhr);
27880             return;
27881         }
27882         
27883         var response = Roo.decode(xhr.responseText);
27884         
27885         this.fireEvent('upload', this, response);
27886         
27887     },
27888     
27889     xhrOnError : function()
27890     {
27891         if(this.loadMask){
27892             this.maskEl.unmask();
27893         }
27894         
27895         Roo.log('xhr on error');
27896         
27897         var response = Roo.decode(xhr.responseText);
27898           
27899         Roo.log(response);
27900         
27901     },
27902     
27903     prepare : function(file)
27904     {   
27905         if(this.loadMask){
27906             this.maskEl.mask(this.loadingText);
27907         }
27908         
27909         this.file = false;
27910         this.exif = {};
27911         
27912         if(typeof(file) === 'string'){
27913             this.loadCanvas(file);
27914             return;
27915         }
27916         
27917         if(!file || !this.urlAPI){
27918             return;
27919         }
27920         
27921         this.file = file;
27922         this.cropType = file.type;
27923         
27924         var _this = this;
27925         
27926         if(this.fireEvent('prepare', this, this.file) != false){
27927             
27928             var reader = new FileReader();
27929             
27930             reader.onload = function (e) {
27931                 if (e.target.error) {
27932                     Roo.log(e.target.error);
27933                     return;
27934                 }
27935                 
27936                 var buffer = e.target.result,
27937                     dataView = new DataView(buffer),
27938                     offset = 2,
27939                     maxOffset = dataView.byteLength - 4,
27940                     markerBytes,
27941                     markerLength;
27942                 
27943                 if (dataView.getUint16(0) === 0xffd8) {
27944                     while (offset < maxOffset) {
27945                         markerBytes = dataView.getUint16(offset);
27946                         
27947                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27948                             markerLength = dataView.getUint16(offset + 2) + 2;
27949                             if (offset + markerLength > dataView.byteLength) {
27950                                 Roo.log('Invalid meta data: Invalid segment size.');
27951                                 break;
27952                             }
27953                             
27954                             if(markerBytes == 0xffe1){
27955                                 _this.parseExifData(
27956                                     dataView,
27957                                     offset,
27958                                     markerLength
27959                                 );
27960                             }
27961                             
27962                             offset += markerLength;
27963                             
27964                             continue;
27965                         }
27966                         
27967                         break;
27968                     }
27969                     
27970                 }
27971                 
27972                 var url = _this.urlAPI.createObjectURL(_this.file);
27973                 
27974                 _this.loadCanvas(url);
27975                 
27976                 return;
27977             }
27978             
27979             reader.readAsArrayBuffer(this.file);
27980             
27981         }
27982         
27983     },
27984     
27985     parseExifData : function(dataView, offset, length)
27986     {
27987         var tiffOffset = offset + 10,
27988             littleEndian,
27989             dirOffset;
27990     
27991         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27992             // No Exif data, might be XMP data instead
27993             return;
27994         }
27995         
27996         // Check for the ASCII code for "Exif" (0x45786966):
27997         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27998             // No Exif data, might be XMP data instead
27999             return;
28000         }
28001         if (tiffOffset + 8 > dataView.byteLength) {
28002             Roo.log('Invalid Exif data: Invalid segment size.');
28003             return;
28004         }
28005         // Check for the two null bytes:
28006         if (dataView.getUint16(offset + 8) !== 0x0000) {
28007             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28008             return;
28009         }
28010         // Check the byte alignment:
28011         switch (dataView.getUint16(tiffOffset)) {
28012         case 0x4949:
28013             littleEndian = true;
28014             break;
28015         case 0x4D4D:
28016             littleEndian = false;
28017             break;
28018         default:
28019             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28020             return;
28021         }
28022         // Check for the TIFF tag marker (0x002A):
28023         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28024             Roo.log('Invalid Exif data: Missing TIFF marker.');
28025             return;
28026         }
28027         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28028         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28029         
28030         this.parseExifTags(
28031             dataView,
28032             tiffOffset,
28033             tiffOffset + dirOffset,
28034             littleEndian
28035         );
28036     },
28037     
28038     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28039     {
28040         var tagsNumber,
28041             dirEndOffset,
28042             i;
28043         if (dirOffset + 6 > dataView.byteLength) {
28044             Roo.log('Invalid Exif data: Invalid directory offset.');
28045             return;
28046         }
28047         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28048         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28049         if (dirEndOffset + 4 > dataView.byteLength) {
28050             Roo.log('Invalid Exif data: Invalid directory size.');
28051             return;
28052         }
28053         for (i = 0; i < tagsNumber; i += 1) {
28054             this.parseExifTag(
28055                 dataView,
28056                 tiffOffset,
28057                 dirOffset + 2 + 12 * i, // tag offset
28058                 littleEndian
28059             );
28060         }
28061         // Return the offset to the next directory:
28062         return dataView.getUint32(dirEndOffset, littleEndian);
28063     },
28064     
28065     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28066     {
28067         var tag = dataView.getUint16(offset, littleEndian);
28068         
28069         this.exif[tag] = this.getExifValue(
28070             dataView,
28071             tiffOffset,
28072             offset,
28073             dataView.getUint16(offset + 2, littleEndian), // tag type
28074             dataView.getUint32(offset + 4, littleEndian), // tag length
28075             littleEndian
28076         );
28077     },
28078     
28079     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28080     {
28081         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28082             tagSize,
28083             dataOffset,
28084             values,
28085             i,
28086             str,
28087             c;
28088     
28089         if (!tagType) {
28090             Roo.log('Invalid Exif data: Invalid tag type.');
28091             return;
28092         }
28093         
28094         tagSize = tagType.size * length;
28095         // Determine if the value is contained in the dataOffset bytes,
28096         // or if the value at the dataOffset is a pointer to the actual data:
28097         dataOffset = tagSize > 4 ?
28098                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28099         if (dataOffset + tagSize > dataView.byteLength) {
28100             Roo.log('Invalid Exif data: Invalid data offset.');
28101             return;
28102         }
28103         if (length === 1) {
28104             return tagType.getValue(dataView, dataOffset, littleEndian);
28105         }
28106         values = [];
28107         for (i = 0; i < length; i += 1) {
28108             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28109         }
28110         
28111         if (tagType.ascii) {
28112             str = '';
28113             // Concatenate the chars:
28114             for (i = 0; i < values.length; i += 1) {
28115                 c = values[i];
28116                 // Ignore the terminating NULL byte(s):
28117                 if (c === '\u0000') {
28118                     break;
28119                 }
28120                 str += c;
28121             }
28122             return str;
28123         }
28124         return values;
28125     }
28126     
28127 });
28128
28129 Roo.apply(Roo.bootstrap.UploadCropbox, {
28130     tags : {
28131         'Orientation': 0x0112
28132     },
28133     
28134     Orientation: {
28135             1: 0, //'top-left',
28136 //            2: 'top-right',
28137             3: 180, //'bottom-right',
28138 //            4: 'bottom-left',
28139 //            5: 'left-top',
28140             6: 90, //'right-top',
28141 //            7: 'right-bottom',
28142             8: 270 //'left-bottom'
28143     },
28144     
28145     exifTagTypes : {
28146         // byte, 8-bit unsigned int:
28147         1: {
28148             getValue: function (dataView, dataOffset) {
28149                 return dataView.getUint8(dataOffset);
28150             },
28151             size: 1
28152         },
28153         // ascii, 8-bit byte:
28154         2: {
28155             getValue: function (dataView, dataOffset) {
28156                 return String.fromCharCode(dataView.getUint8(dataOffset));
28157             },
28158             size: 1,
28159             ascii: true
28160         },
28161         // short, 16 bit int:
28162         3: {
28163             getValue: function (dataView, dataOffset, littleEndian) {
28164                 return dataView.getUint16(dataOffset, littleEndian);
28165             },
28166             size: 2
28167         },
28168         // long, 32 bit int:
28169         4: {
28170             getValue: function (dataView, dataOffset, littleEndian) {
28171                 return dataView.getUint32(dataOffset, littleEndian);
28172             },
28173             size: 4
28174         },
28175         // rational = two long values, first is numerator, second is denominator:
28176         5: {
28177             getValue: function (dataView, dataOffset, littleEndian) {
28178                 return dataView.getUint32(dataOffset, littleEndian) /
28179                     dataView.getUint32(dataOffset + 4, littleEndian);
28180             },
28181             size: 8
28182         },
28183         // slong, 32 bit signed int:
28184         9: {
28185             getValue: function (dataView, dataOffset, littleEndian) {
28186                 return dataView.getInt32(dataOffset, littleEndian);
28187             },
28188             size: 4
28189         },
28190         // srational, two slongs, first is numerator, second is denominator:
28191         10: {
28192             getValue: function (dataView, dataOffset, littleEndian) {
28193                 return dataView.getInt32(dataOffset, littleEndian) /
28194                     dataView.getInt32(dataOffset + 4, littleEndian);
28195             },
28196             size: 8
28197         }
28198     },
28199     
28200     footer : {
28201         STANDARD : [
28202             {
28203                 tag : 'div',
28204                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28205                 action : 'rotate-left',
28206                 cn : [
28207                     {
28208                         tag : 'button',
28209                         cls : 'btn btn-default',
28210                         html : '<i class="fa fa-undo"></i>'
28211                     }
28212                 ]
28213             },
28214             {
28215                 tag : 'div',
28216                 cls : 'btn-group roo-upload-cropbox-picture',
28217                 action : 'picture',
28218                 cn : [
28219                     {
28220                         tag : 'button',
28221                         cls : 'btn btn-default',
28222                         html : '<i class="fa fa-picture-o"></i>'
28223                     }
28224                 ]
28225             },
28226             {
28227                 tag : 'div',
28228                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28229                 action : 'rotate-right',
28230                 cn : [
28231                     {
28232                         tag : 'button',
28233                         cls : 'btn btn-default',
28234                         html : '<i class="fa fa-repeat"></i>'
28235                     }
28236                 ]
28237             }
28238         ],
28239         DOCUMENT : [
28240             {
28241                 tag : 'div',
28242                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28243                 action : 'rotate-left',
28244                 cn : [
28245                     {
28246                         tag : 'button',
28247                         cls : 'btn btn-default',
28248                         html : '<i class="fa fa-undo"></i>'
28249                     }
28250                 ]
28251             },
28252             {
28253                 tag : 'div',
28254                 cls : 'btn-group roo-upload-cropbox-download',
28255                 action : 'download',
28256                 cn : [
28257                     {
28258                         tag : 'button',
28259                         cls : 'btn btn-default',
28260                         html : '<i class="fa fa-download"></i>'
28261                     }
28262                 ]
28263             },
28264             {
28265                 tag : 'div',
28266                 cls : 'btn-group roo-upload-cropbox-crop',
28267                 action : 'crop',
28268                 cn : [
28269                     {
28270                         tag : 'button',
28271                         cls : 'btn btn-default',
28272                         html : '<i class="fa fa-crop"></i>'
28273                     }
28274                 ]
28275             },
28276             {
28277                 tag : 'div',
28278                 cls : 'btn-group roo-upload-cropbox-trash',
28279                 action : 'trash',
28280                 cn : [
28281                     {
28282                         tag : 'button',
28283                         cls : 'btn btn-default',
28284                         html : '<i class="fa fa-trash"></i>'
28285                     }
28286                 ]
28287             },
28288             {
28289                 tag : 'div',
28290                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28291                 action : 'rotate-right',
28292                 cn : [
28293                     {
28294                         tag : 'button',
28295                         cls : 'btn btn-default',
28296                         html : '<i class="fa fa-repeat"></i>'
28297                     }
28298                 ]
28299             }
28300         ],
28301         ROTATOR : [
28302             {
28303                 tag : 'div',
28304                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28305                 action : 'rotate-left',
28306                 cn : [
28307                     {
28308                         tag : 'button',
28309                         cls : 'btn btn-default',
28310                         html : '<i class="fa fa-undo"></i>'
28311                     }
28312                 ]
28313             },
28314             {
28315                 tag : 'div',
28316                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28317                 action : 'rotate-right',
28318                 cn : [
28319                     {
28320                         tag : 'button',
28321                         cls : 'btn btn-default',
28322                         html : '<i class="fa fa-repeat"></i>'
28323                     }
28324                 ]
28325             }
28326         ]
28327     }
28328 });
28329
28330 /*
28331 * Licence: LGPL
28332 */
28333
28334 /**
28335  * @class Roo.bootstrap.DocumentManager
28336  * @extends Roo.bootstrap.Component
28337  * Bootstrap DocumentManager class
28338  * @cfg {String} paramName default 'imageUpload'
28339  * @cfg {String} toolTipName default 'filename'
28340  * @cfg {String} method default POST
28341  * @cfg {String} url action url
28342  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28343  * @cfg {Boolean} multiple multiple upload default true
28344  * @cfg {Number} thumbSize default 300
28345  * @cfg {String} fieldLabel
28346  * @cfg {Number} labelWidth default 4
28347  * @cfg {String} labelAlign (left|top) default left
28348  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28349 * @cfg {Number} labellg set the width of label (1-12)
28350  * @cfg {Number} labelmd set the width of label (1-12)
28351  * @cfg {Number} labelsm set the width of label (1-12)
28352  * @cfg {Number} labelxs set the width of label (1-12)
28353  * 
28354  * @constructor
28355  * Create a new DocumentManager
28356  * @param {Object} config The config object
28357  */
28358
28359 Roo.bootstrap.DocumentManager = function(config){
28360     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28361     
28362     this.files = [];
28363     this.delegates = [];
28364     
28365     this.addEvents({
28366         /**
28367          * @event initial
28368          * Fire when initial the DocumentManager
28369          * @param {Roo.bootstrap.DocumentManager} this
28370          */
28371         "initial" : true,
28372         /**
28373          * @event inspect
28374          * inspect selected file
28375          * @param {Roo.bootstrap.DocumentManager} this
28376          * @param {File} file
28377          */
28378         "inspect" : true,
28379         /**
28380          * @event exception
28381          * Fire when xhr load exception
28382          * @param {Roo.bootstrap.DocumentManager} this
28383          * @param {XMLHttpRequest} xhr
28384          */
28385         "exception" : true,
28386         /**
28387          * @event afterupload
28388          * Fire when xhr load exception
28389          * @param {Roo.bootstrap.DocumentManager} this
28390          * @param {XMLHttpRequest} xhr
28391          */
28392         "afterupload" : true,
28393         /**
28394          * @event prepare
28395          * prepare the form data
28396          * @param {Roo.bootstrap.DocumentManager} this
28397          * @param {Object} formData
28398          */
28399         "prepare" : true,
28400         /**
28401          * @event remove
28402          * Fire when remove the file
28403          * @param {Roo.bootstrap.DocumentManager} this
28404          * @param {Object} file
28405          */
28406         "remove" : true,
28407         /**
28408          * @event refresh
28409          * Fire after refresh the file
28410          * @param {Roo.bootstrap.DocumentManager} this
28411          */
28412         "refresh" : true,
28413         /**
28414          * @event click
28415          * Fire after click the image
28416          * @param {Roo.bootstrap.DocumentManager} this
28417          * @param {Object} file
28418          */
28419         "click" : true,
28420         /**
28421          * @event edit
28422          * Fire when upload a image and editable set to true
28423          * @param {Roo.bootstrap.DocumentManager} this
28424          * @param {Object} file
28425          */
28426         "edit" : true,
28427         /**
28428          * @event beforeselectfile
28429          * Fire before select file
28430          * @param {Roo.bootstrap.DocumentManager} this
28431          */
28432         "beforeselectfile" : true,
28433         /**
28434          * @event process
28435          * Fire before process file
28436          * @param {Roo.bootstrap.DocumentManager} this
28437          * @param {Object} file
28438          */
28439         "process" : true,
28440         /**
28441          * @event previewrendered
28442          * Fire when preview rendered
28443          * @param {Roo.bootstrap.DocumentManager} this
28444          * @param {Object} file
28445          */
28446         "previewrendered" : true
28447         
28448     });
28449 };
28450
28451 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28452     
28453     boxes : 0,
28454     inputName : '',
28455     thumbSize : 300,
28456     multiple : true,
28457     files : false,
28458     method : 'POST',
28459     url : '',
28460     paramName : 'imageUpload',
28461     toolTipName : 'filename',
28462     fieldLabel : '',
28463     labelWidth : 4,
28464     labelAlign : 'left',
28465     editable : true,
28466     delegates : false,
28467     xhr : false, 
28468     
28469     labellg : 0,
28470     labelmd : 0,
28471     labelsm : 0,
28472     labelxs : 0,
28473     
28474     getAutoCreate : function()
28475     {   
28476         var managerWidget = {
28477             tag : 'div',
28478             cls : 'roo-document-manager',
28479             cn : [
28480                 {
28481                     tag : 'input',
28482                     cls : 'roo-document-manager-selector',
28483                     type : 'file'
28484                 },
28485                 {
28486                     tag : 'div',
28487                     cls : 'roo-document-manager-uploader',
28488                     cn : [
28489                         {
28490                             tag : 'div',
28491                             cls : 'roo-document-manager-upload-btn',
28492                             html : '<i class="fa fa-plus"></i>'
28493                         }
28494                     ]
28495                     
28496                 }
28497             ]
28498         };
28499         
28500         var content = [
28501             {
28502                 tag : 'div',
28503                 cls : 'column col-md-12',
28504                 cn : managerWidget
28505             }
28506         ];
28507         
28508         if(this.fieldLabel.length){
28509             
28510             content = [
28511                 {
28512                     tag : 'div',
28513                     cls : 'column col-md-12',
28514                     html : this.fieldLabel
28515                 },
28516                 {
28517                     tag : 'div',
28518                     cls : 'column col-md-12',
28519                     cn : managerWidget
28520                 }
28521             ];
28522
28523             if(this.labelAlign == 'left'){
28524                 content = [
28525                     {
28526                         tag : 'div',
28527                         cls : 'column',
28528                         html : this.fieldLabel
28529                     },
28530                     {
28531                         tag : 'div',
28532                         cls : 'column',
28533                         cn : managerWidget
28534                     }
28535                 ];
28536                 
28537                 if(this.labelWidth > 12){
28538                     content[0].style = "width: " + this.labelWidth + 'px';
28539                 }
28540
28541                 if(this.labelWidth < 13 && this.labelmd == 0){
28542                     this.labelmd = this.labelWidth;
28543                 }
28544
28545                 if(this.labellg > 0){
28546                     content[0].cls += ' col-lg-' + this.labellg;
28547                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28548                 }
28549
28550                 if(this.labelmd > 0){
28551                     content[0].cls += ' col-md-' + this.labelmd;
28552                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28553                 }
28554
28555                 if(this.labelsm > 0){
28556                     content[0].cls += ' col-sm-' + this.labelsm;
28557                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28558                 }
28559
28560                 if(this.labelxs > 0){
28561                     content[0].cls += ' col-xs-' + this.labelxs;
28562                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28563                 }
28564                 
28565             }
28566         }
28567         
28568         var cfg = {
28569             tag : 'div',
28570             cls : 'row clearfix',
28571             cn : content
28572         };
28573         
28574         return cfg;
28575         
28576     },
28577     
28578     initEvents : function()
28579     {
28580         this.managerEl = this.el.select('.roo-document-manager', true).first();
28581         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28582         
28583         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28584         this.selectorEl.hide();
28585         
28586         if(this.multiple){
28587             this.selectorEl.attr('multiple', 'multiple');
28588         }
28589         
28590         this.selectorEl.on('change', this.onFileSelected, this);
28591         
28592         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28593         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28594         
28595         this.uploader.on('click', this.onUploaderClick, this);
28596         
28597         this.renderProgressDialog();
28598         
28599         var _this = this;
28600         
28601         window.addEventListener("resize", function() { _this.refresh(); } );
28602         
28603         this.fireEvent('initial', this);
28604     },
28605     
28606     renderProgressDialog : function()
28607     {
28608         var _this = this;
28609         
28610         this.progressDialog = new Roo.bootstrap.Modal({
28611             cls : 'roo-document-manager-progress-dialog',
28612             allow_close : false,
28613             title : '',
28614             buttons : [
28615                 {
28616                     name  :'cancel',
28617                     weight : 'danger',
28618                     html : 'Cancel'
28619                 }
28620             ], 
28621             listeners : { 
28622                 btnclick : function() {
28623                     _this.uploadCancel();
28624                     this.hide();
28625                 }
28626             }
28627         });
28628          
28629         this.progressDialog.render(Roo.get(document.body));
28630          
28631         this.progress = new Roo.bootstrap.Progress({
28632             cls : 'roo-document-manager-progress',
28633             active : true,
28634             striped : true
28635         });
28636         
28637         this.progress.render(this.progressDialog.getChildContainer());
28638         
28639         this.progressBar = new Roo.bootstrap.ProgressBar({
28640             cls : 'roo-document-manager-progress-bar',
28641             aria_valuenow : 0,
28642             aria_valuemin : 0,
28643             aria_valuemax : 12,
28644             panel : 'success'
28645         });
28646         
28647         this.progressBar.render(this.progress.getChildContainer());
28648     },
28649     
28650     onUploaderClick : function(e)
28651     {
28652         e.preventDefault();
28653      
28654         if(this.fireEvent('beforeselectfile', this) != false){
28655             this.selectorEl.dom.click();
28656         }
28657         
28658     },
28659     
28660     onFileSelected : function(e)
28661     {
28662         e.preventDefault();
28663         
28664         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28665             return;
28666         }
28667         
28668         Roo.each(this.selectorEl.dom.files, function(file){
28669             if(this.fireEvent('inspect', this, file) != false){
28670                 this.files.push(file);
28671             }
28672         }, this);
28673         
28674         this.queue();
28675         
28676     },
28677     
28678     queue : function()
28679     {
28680         this.selectorEl.dom.value = '';
28681         
28682         if(!this.files || !this.files.length){
28683             return;
28684         }
28685         
28686         if(this.boxes > 0 && this.files.length > this.boxes){
28687             this.files = this.files.slice(0, this.boxes);
28688         }
28689         
28690         this.uploader.show();
28691         
28692         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28693             this.uploader.hide();
28694         }
28695         
28696         var _this = this;
28697         
28698         var files = [];
28699         
28700         var docs = [];
28701         
28702         Roo.each(this.files, function(file){
28703             
28704             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28705                 var f = this.renderPreview(file);
28706                 files.push(f);
28707                 return;
28708             }
28709             
28710             if(file.type.indexOf('image') != -1){
28711                 this.delegates.push(
28712                     (function(){
28713                         _this.process(file);
28714                     }).createDelegate(this)
28715                 );
28716         
28717                 return;
28718             }
28719             
28720             docs.push(
28721                 (function(){
28722                     _this.process(file);
28723                 }).createDelegate(this)
28724             );
28725             
28726         }, this);
28727         
28728         this.files = files;
28729         
28730         this.delegates = this.delegates.concat(docs);
28731         
28732         if(!this.delegates.length){
28733             this.refresh();
28734             return;
28735         }
28736         
28737         this.progressBar.aria_valuemax = this.delegates.length;
28738         
28739         this.arrange();
28740         
28741         return;
28742     },
28743     
28744     arrange : function()
28745     {
28746         if(!this.delegates.length){
28747             this.progressDialog.hide();
28748             this.refresh();
28749             return;
28750         }
28751         
28752         var delegate = this.delegates.shift();
28753         
28754         this.progressDialog.show();
28755         
28756         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28757         
28758         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28759         
28760         delegate();
28761     },
28762     
28763     refresh : function()
28764     {
28765         this.uploader.show();
28766         
28767         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28768             this.uploader.hide();
28769         }
28770         
28771         Roo.isTouch ? this.closable(false) : this.closable(true);
28772         
28773         this.fireEvent('refresh', this);
28774     },
28775     
28776     onRemove : function(e, el, o)
28777     {
28778         e.preventDefault();
28779         
28780         this.fireEvent('remove', this, o);
28781         
28782     },
28783     
28784     remove : function(o)
28785     {
28786         var files = [];
28787         
28788         Roo.each(this.files, function(file){
28789             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28790                 files.push(file);
28791                 return;
28792             }
28793
28794             o.target.remove();
28795
28796         }, this);
28797         
28798         this.files = files;
28799         
28800         this.refresh();
28801     },
28802     
28803     clear : function()
28804     {
28805         Roo.each(this.files, function(file){
28806             if(!file.target){
28807                 return;
28808             }
28809             
28810             file.target.remove();
28811
28812         }, this);
28813         
28814         this.files = [];
28815         
28816         this.refresh();
28817     },
28818     
28819     onClick : function(e, el, o)
28820     {
28821         e.preventDefault();
28822         
28823         this.fireEvent('click', this, o);
28824         
28825     },
28826     
28827     closable : function(closable)
28828     {
28829         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28830             
28831             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28832             
28833             if(closable){
28834                 el.show();
28835                 return;
28836             }
28837             
28838             el.hide();
28839             
28840         }, this);
28841     },
28842     
28843     xhrOnLoad : function(xhr)
28844     {
28845         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28846             el.remove();
28847         }, this);
28848         
28849         if (xhr.readyState !== 4) {
28850             this.arrange();
28851             this.fireEvent('exception', this, xhr);
28852             return;
28853         }
28854
28855         var response = Roo.decode(xhr.responseText);
28856         
28857         if(!response.success){
28858             this.arrange();
28859             this.fireEvent('exception', this, xhr);
28860             return;
28861         }
28862         
28863         var file = this.renderPreview(response.data);
28864         
28865         this.files.push(file);
28866         
28867         this.arrange();
28868         
28869         this.fireEvent('afterupload', this, xhr);
28870         
28871     },
28872     
28873     xhrOnError : function(xhr)
28874     {
28875         Roo.log('xhr on error');
28876         
28877         var response = Roo.decode(xhr.responseText);
28878           
28879         Roo.log(response);
28880         
28881         this.arrange();
28882     },
28883     
28884     process : function(file)
28885     {
28886         if(this.fireEvent('process', this, file) !== false){
28887             if(this.editable && file.type.indexOf('image') != -1){
28888                 this.fireEvent('edit', this, file);
28889                 return;
28890             }
28891
28892             this.uploadStart(file, false);
28893
28894             return;
28895         }
28896         
28897     },
28898     
28899     uploadStart : function(file, crop)
28900     {
28901         this.xhr = new XMLHttpRequest();
28902         
28903         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28904             this.arrange();
28905             return;
28906         }
28907         
28908         file.xhr = this.xhr;
28909             
28910         this.managerEl.createChild({
28911             tag : 'div',
28912             cls : 'roo-document-manager-loading',
28913             cn : [
28914                 {
28915                     tag : 'div',
28916                     tooltip : file.name,
28917                     cls : 'roo-document-manager-thumb',
28918                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28919                 }
28920             ]
28921
28922         });
28923
28924         this.xhr.open(this.method, this.url, true);
28925         
28926         var headers = {
28927             "Accept": "application/json",
28928             "Cache-Control": "no-cache",
28929             "X-Requested-With": "XMLHttpRequest"
28930         };
28931         
28932         for (var headerName in headers) {
28933             var headerValue = headers[headerName];
28934             if (headerValue) {
28935                 this.xhr.setRequestHeader(headerName, headerValue);
28936             }
28937         }
28938         
28939         var _this = this;
28940         
28941         this.xhr.onload = function()
28942         {
28943             _this.xhrOnLoad(_this.xhr);
28944         }
28945         
28946         this.xhr.onerror = function()
28947         {
28948             _this.xhrOnError(_this.xhr);
28949         }
28950         
28951         var formData = new FormData();
28952
28953         formData.append('returnHTML', 'NO');
28954         
28955         if(crop){
28956             formData.append('crop', crop);
28957         }
28958         
28959         formData.append(this.paramName, file, file.name);
28960         
28961         var options = {
28962             file : file, 
28963             manually : false
28964         };
28965         
28966         if(this.fireEvent('prepare', this, formData, options) != false){
28967             
28968             if(options.manually){
28969                 return;
28970             }
28971             
28972             this.xhr.send(formData);
28973             return;
28974         };
28975         
28976         this.uploadCancel();
28977     },
28978     
28979     uploadCancel : function()
28980     {
28981         if (this.xhr) {
28982             this.xhr.abort();
28983         }
28984         
28985         this.delegates = [];
28986         
28987         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28988             el.remove();
28989         }, this);
28990         
28991         this.arrange();
28992     },
28993     
28994     renderPreview : function(file)
28995     {
28996         if(typeof(file.target) != 'undefined' && file.target){
28997             return file;
28998         }
28999         
29000         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29001         
29002         var previewEl = this.managerEl.createChild({
29003             tag : 'div',
29004             cls : 'roo-document-manager-preview',
29005             cn : [
29006                 {
29007                     tag : 'div',
29008                     tooltip : file[this.toolTipName],
29009                     cls : 'roo-document-manager-thumb',
29010                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29011                 },
29012                 {
29013                     tag : 'button',
29014                     cls : 'close',
29015                     html : '<i class="fa fa-times-circle"></i>'
29016                 }
29017             ]
29018         });
29019
29020         var close = previewEl.select('button.close', true).first();
29021
29022         close.on('click', this.onRemove, this, file);
29023
29024         file.target = previewEl;
29025
29026         var image = previewEl.select('img', true).first();
29027         
29028         var _this = this;
29029         
29030         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29031         
29032         image.on('click', this.onClick, this, file);
29033         
29034         this.fireEvent('previewrendered', this, file);
29035         
29036         return file;
29037         
29038     },
29039     
29040     onPreviewLoad : function(file, image)
29041     {
29042         if(typeof(file.target) == 'undefined' || !file.target){
29043             return;
29044         }
29045         
29046         var width = image.dom.naturalWidth || image.dom.width;
29047         var height = image.dom.naturalHeight || image.dom.height;
29048         
29049         if(width > height){
29050             file.target.addClass('wide');
29051             return;
29052         }
29053         
29054         file.target.addClass('tall');
29055         return;
29056         
29057     },
29058     
29059     uploadFromSource : function(file, crop)
29060     {
29061         this.xhr = new XMLHttpRequest();
29062         
29063         this.managerEl.createChild({
29064             tag : 'div',
29065             cls : 'roo-document-manager-loading',
29066             cn : [
29067                 {
29068                     tag : 'div',
29069                     tooltip : file.name,
29070                     cls : 'roo-document-manager-thumb',
29071                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29072                 }
29073             ]
29074
29075         });
29076
29077         this.xhr.open(this.method, this.url, true);
29078         
29079         var headers = {
29080             "Accept": "application/json",
29081             "Cache-Control": "no-cache",
29082             "X-Requested-With": "XMLHttpRequest"
29083         };
29084         
29085         for (var headerName in headers) {
29086             var headerValue = headers[headerName];
29087             if (headerValue) {
29088                 this.xhr.setRequestHeader(headerName, headerValue);
29089             }
29090         }
29091         
29092         var _this = this;
29093         
29094         this.xhr.onload = function()
29095         {
29096             _this.xhrOnLoad(_this.xhr);
29097         }
29098         
29099         this.xhr.onerror = function()
29100         {
29101             _this.xhrOnError(_this.xhr);
29102         }
29103         
29104         var formData = new FormData();
29105
29106         formData.append('returnHTML', 'NO');
29107         
29108         formData.append('crop', crop);
29109         
29110         if(typeof(file.filename) != 'undefined'){
29111             formData.append('filename', file.filename);
29112         }
29113         
29114         if(typeof(file.mimetype) != 'undefined'){
29115             formData.append('mimetype', file.mimetype);
29116         }
29117         
29118         Roo.log(formData);
29119         
29120         if(this.fireEvent('prepare', this, formData) != false){
29121             this.xhr.send(formData);
29122         };
29123     }
29124 });
29125
29126 /*
29127 * Licence: LGPL
29128 */
29129
29130 /**
29131  * @class Roo.bootstrap.DocumentViewer
29132  * @extends Roo.bootstrap.Component
29133  * Bootstrap DocumentViewer class
29134  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29135  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29136  * 
29137  * @constructor
29138  * Create a new DocumentViewer
29139  * @param {Object} config The config object
29140  */
29141
29142 Roo.bootstrap.DocumentViewer = function(config){
29143     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29144     
29145     this.addEvents({
29146         /**
29147          * @event initial
29148          * Fire after initEvent
29149          * @param {Roo.bootstrap.DocumentViewer} this
29150          */
29151         "initial" : true,
29152         /**
29153          * @event click
29154          * Fire after click
29155          * @param {Roo.bootstrap.DocumentViewer} this
29156          */
29157         "click" : true,
29158         /**
29159          * @event download
29160          * Fire after download button
29161          * @param {Roo.bootstrap.DocumentViewer} this
29162          */
29163         "download" : true,
29164         /**
29165          * @event trash
29166          * Fire after trash button
29167          * @param {Roo.bootstrap.DocumentViewer} this
29168          */
29169         "trash" : true
29170         
29171     });
29172 };
29173
29174 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29175     
29176     showDownload : true,
29177     
29178     showTrash : true,
29179     
29180     getAutoCreate : function()
29181     {
29182         var cfg = {
29183             tag : 'div',
29184             cls : 'roo-document-viewer',
29185             cn : [
29186                 {
29187                     tag : 'div',
29188                     cls : 'roo-document-viewer-body',
29189                     cn : [
29190                         {
29191                             tag : 'div',
29192                             cls : 'roo-document-viewer-thumb',
29193                             cn : [
29194                                 {
29195                                     tag : 'img',
29196                                     cls : 'roo-document-viewer-image'
29197                                 }
29198                             ]
29199                         }
29200                     ]
29201                 },
29202                 {
29203                     tag : 'div',
29204                     cls : 'roo-document-viewer-footer',
29205                     cn : {
29206                         tag : 'div',
29207                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29208                         cn : [
29209                             {
29210                                 tag : 'div',
29211                                 cls : 'btn-group roo-document-viewer-download',
29212                                 cn : [
29213                                     {
29214                                         tag : 'button',
29215                                         cls : 'btn btn-default',
29216                                         html : '<i class="fa fa-download"></i>'
29217                                     }
29218                                 ]
29219                             },
29220                             {
29221                                 tag : 'div',
29222                                 cls : 'btn-group roo-document-viewer-trash',
29223                                 cn : [
29224                                     {
29225                                         tag : 'button',
29226                                         cls : 'btn btn-default',
29227                                         html : '<i class="fa fa-trash"></i>'
29228                                     }
29229                                 ]
29230                             }
29231                         ]
29232                     }
29233                 }
29234             ]
29235         };
29236         
29237         return cfg;
29238     },
29239     
29240     initEvents : function()
29241     {
29242         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29243         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29244         
29245         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29246         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29247         
29248         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29249         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29250         
29251         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29252         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29253         
29254         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29255         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29256         
29257         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29258         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29259         
29260         this.bodyEl.on('click', this.onClick, this);
29261         this.downloadBtn.on('click', this.onDownload, this);
29262         this.trashBtn.on('click', this.onTrash, this);
29263         
29264         this.downloadBtn.hide();
29265         this.trashBtn.hide();
29266         
29267         if(this.showDownload){
29268             this.downloadBtn.show();
29269         }
29270         
29271         if(this.showTrash){
29272             this.trashBtn.show();
29273         }
29274         
29275         if(!this.showDownload && !this.showTrash) {
29276             this.footerEl.hide();
29277         }
29278         
29279     },
29280     
29281     initial : function()
29282     {
29283         this.fireEvent('initial', this);
29284         
29285     },
29286     
29287     onClick : function(e)
29288     {
29289         e.preventDefault();
29290         
29291         this.fireEvent('click', this);
29292     },
29293     
29294     onDownload : function(e)
29295     {
29296         e.preventDefault();
29297         
29298         this.fireEvent('download', this);
29299     },
29300     
29301     onTrash : function(e)
29302     {
29303         e.preventDefault();
29304         
29305         this.fireEvent('trash', this);
29306     }
29307     
29308 });
29309 /*
29310  * - LGPL
29311  *
29312  * nav progress bar
29313  * 
29314  */
29315
29316 /**
29317  * @class Roo.bootstrap.NavProgressBar
29318  * @extends Roo.bootstrap.Component
29319  * Bootstrap NavProgressBar class
29320  * 
29321  * @constructor
29322  * Create a new nav progress bar
29323  * @param {Object} config The config object
29324  */
29325
29326 Roo.bootstrap.NavProgressBar = function(config){
29327     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29328
29329     this.bullets = this.bullets || [];
29330    
29331 //    Roo.bootstrap.NavProgressBar.register(this);
29332      this.addEvents({
29333         /**
29334              * @event changed
29335              * Fires when the active item changes
29336              * @param {Roo.bootstrap.NavProgressBar} this
29337              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29338              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29339          */
29340         'changed': true
29341      });
29342     
29343 };
29344
29345 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29346     
29347     bullets : [],
29348     barItems : [],
29349     
29350     getAutoCreate : function()
29351     {
29352         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29353         
29354         cfg = {
29355             tag : 'div',
29356             cls : 'roo-navigation-bar-group',
29357             cn : [
29358                 {
29359                     tag : 'div',
29360                     cls : 'roo-navigation-top-bar'
29361                 },
29362                 {
29363                     tag : 'div',
29364                     cls : 'roo-navigation-bullets-bar',
29365                     cn : [
29366                         {
29367                             tag : 'ul',
29368                             cls : 'roo-navigation-bar'
29369                         }
29370                     ]
29371                 },
29372                 
29373                 {
29374                     tag : 'div',
29375                     cls : 'roo-navigation-bottom-bar'
29376                 }
29377             ]
29378             
29379         };
29380         
29381         return cfg;
29382         
29383     },
29384     
29385     initEvents: function() 
29386     {
29387         
29388     },
29389     
29390     onRender : function(ct, position) 
29391     {
29392         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29393         
29394         if(this.bullets.length){
29395             Roo.each(this.bullets, function(b){
29396                this.addItem(b);
29397             }, this);
29398         }
29399         
29400         this.format();
29401         
29402     },
29403     
29404     addItem : function(cfg)
29405     {
29406         var item = new Roo.bootstrap.NavProgressItem(cfg);
29407         
29408         item.parentId = this.id;
29409         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29410         
29411         if(cfg.html){
29412             var top = new Roo.bootstrap.Element({
29413                 tag : 'div',
29414                 cls : 'roo-navigation-bar-text'
29415             });
29416             
29417             var bottom = new Roo.bootstrap.Element({
29418                 tag : 'div',
29419                 cls : 'roo-navigation-bar-text'
29420             });
29421             
29422             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29423             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29424             
29425             var topText = new Roo.bootstrap.Element({
29426                 tag : 'span',
29427                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29428             });
29429             
29430             var bottomText = new Roo.bootstrap.Element({
29431                 tag : 'span',
29432                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29433             });
29434             
29435             topText.onRender(top.el, null);
29436             bottomText.onRender(bottom.el, null);
29437             
29438             item.topEl = top;
29439             item.bottomEl = bottom;
29440         }
29441         
29442         this.barItems.push(item);
29443         
29444         return item;
29445     },
29446     
29447     getActive : function()
29448     {
29449         var active = false;
29450         
29451         Roo.each(this.barItems, function(v){
29452             
29453             if (!v.isActive()) {
29454                 return;
29455             }
29456             
29457             active = v;
29458             return false;
29459             
29460         });
29461         
29462         return active;
29463     },
29464     
29465     setActiveItem : function(item)
29466     {
29467         var prev = false;
29468         
29469         Roo.each(this.barItems, function(v){
29470             if (v.rid == item.rid) {
29471                 return ;
29472             }
29473             
29474             if (v.isActive()) {
29475                 v.setActive(false);
29476                 prev = v;
29477             }
29478         });
29479
29480         item.setActive(true);
29481         
29482         this.fireEvent('changed', this, item, prev);
29483     },
29484     
29485     getBarItem: function(rid)
29486     {
29487         var ret = false;
29488         
29489         Roo.each(this.barItems, function(e) {
29490             if (e.rid != rid) {
29491                 return;
29492             }
29493             
29494             ret =  e;
29495             return false;
29496         });
29497         
29498         return ret;
29499     },
29500     
29501     indexOfItem : function(item)
29502     {
29503         var index = false;
29504         
29505         Roo.each(this.barItems, function(v, i){
29506             
29507             if (v.rid != item.rid) {
29508                 return;
29509             }
29510             
29511             index = i;
29512             return false
29513         });
29514         
29515         return index;
29516     },
29517     
29518     setActiveNext : function()
29519     {
29520         var i = this.indexOfItem(this.getActive());
29521         
29522         if (i > this.barItems.length) {
29523             return;
29524         }
29525         
29526         this.setActiveItem(this.barItems[i+1]);
29527     },
29528     
29529     setActivePrev : function()
29530     {
29531         var i = this.indexOfItem(this.getActive());
29532         
29533         if (i  < 1) {
29534             return;
29535         }
29536         
29537         this.setActiveItem(this.barItems[i-1]);
29538     },
29539     
29540     format : function()
29541     {
29542         if(!this.barItems.length){
29543             return;
29544         }
29545      
29546         var width = 100 / this.barItems.length;
29547         
29548         Roo.each(this.barItems, function(i){
29549             i.el.setStyle('width', width + '%');
29550             i.topEl.el.setStyle('width', width + '%');
29551             i.bottomEl.el.setStyle('width', width + '%');
29552         }, this);
29553         
29554     }
29555     
29556 });
29557 /*
29558  * - LGPL
29559  *
29560  * Nav Progress Item
29561  * 
29562  */
29563
29564 /**
29565  * @class Roo.bootstrap.NavProgressItem
29566  * @extends Roo.bootstrap.Component
29567  * Bootstrap NavProgressItem class
29568  * @cfg {String} rid the reference id
29569  * @cfg {Boolean} active (true|false) Is item active default false
29570  * @cfg {Boolean} disabled (true|false) Is item active default false
29571  * @cfg {String} html
29572  * @cfg {String} position (top|bottom) text position default bottom
29573  * @cfg {String} icon show icon instead of number
29574  * 
29575  * @constructor
29576  * Create a new NavProgressItem
29577  * @param {Object} config The config object
29578  */
29579 Roo.bootstrap.NavProgressItem = function(config){
29580     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29581     this.addEvents({
29582         // raw events
29583         /**
29584          * @event click
29585          * The raw click event for the entire grid.
29586          * @param {Roo.bootstrap.NavProgressItem} this
29587          * @param {Roo.EventObject} e
29588          */
29589         "click" : true
29590     });
29591    
29592 };
29593
29594 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29595     
29596     rid : '',
29597     active : false,
29598     disabled : false,
29599     html : '',
29600     position : 'bottom',
29601     icon : false,
29602     
29603     getAutoCreate : function()
29604     {
29605         var iconCls = 'roo-navigation-bar-item-icon';
29606         
29607         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29608         
29609         var cfg = {
29610             tag: 'li',
29611             cls: 'roo-navigation-bar-item',
29612             cn : [
29613                 {
29614                     tag : 'i',
29615                     cls : iconCls
29616                 }
29617             ]
29618         };
29619         
29620         if(this.active){
29621             cfg.cls += ' active';
29622         }
29623         if(this.disabled){
29624             cfg.cls += ' disabled';
29625         }
29626         
29627         return cfg;
29628     },
29629     
29630     disable : function()
29631     {
29632         this.setDisabled(true);
29633     },
29634     
29635     enable : function()
29636     {
29637         this.setDisabled(false);
29638     },
29639     
29640     initEvents: function() 
29641     {
29642         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29643         
29644         this.iconEl.on('click', this.onClick, this);
29645     },
29646     
29647     onClick : function(e)
29648     {
29649         e.preventDefault();
29650         
29651         if(this.disabled){
29652             return;
29653         }
29654         
29655         if(this.fireEvent('click', this, e) === false){
29656             return;
29657         };
29658         
29659         this.parent().setActiveItem(this);
29660     },
29661     
29662     isActive: function () 
29663     {
29664         return this.active;
29665     },
29666     
29667     setActive : function(state)
29668     {
29669         if(this.active == state){
29670             return;
29671         }
29672         
29673         this.active = state;
29674         
29675         if (state) {
29676             this.el.addClass('active');
29677             return;
29678         }
29679         
29680         this.el.removeClass('active');
29681         
29682         return;
29683     },
29684     
29685     setDisabled : function(state)
29686     {
29687         if(this.disabled == state){
29688             return;
29689         }
29690         
29691         this.disabled = state;
29692         
29693         if (state) {
29694             this.el.addClass('disabled');
29695             return;
29696         }
29697         
29698         this.el.removeClass('disabled');
29699     },
29700     
29701     tooltipEl : function()
29702     {
29703         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29704     }
29705 });
29706  
29707
29708  /*
29709  * - LGPL
29710  *
29711  * FieldLabel
29712  * 
29713  */
29714
29715 /**
29716  * @class Roo.bootstrap.FieldLabel
29717  * @extends Roo.bootstrap.Component
29718  * Bootstrap FieldLabel class
29719  * @cfg {String} html contents of the element
29720  * @cfg {String} tag tag of the element default label
29721  * @cfg {String} cls class of the element
29722  * @cfg {String} target label target 
29723  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29724  * @cfg {String} invalidClass default "text-warning"
29725  * @cfg {String} validClass default "text-success"
29726  * @cfg {String} iconTooltip default "This field is required"
29727  * @cfg {String} indicatorpos (left|right) default left
29728  * 
29729  * @constructor
29730  * Create a new FieldLabel
29731  * @param {Object} config The config object
29732  */
29733
29734 Roo.bootstrap.FieldLabel = function(config){
29735     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29736     
29737     this.addEvents({
29738             /**
29739              * @event invalid
29740              * Fires after the field has been marked as invalid.
29741              * @param {Roo.form.FieldLabel} this
29742              * @param {String} msg The validation message
29743              */
29744             invalid : true,
29745             /**
29746              * @event valid
29747              * Fires after the field has been validated with no errors.
29748              * @param {Roo.form.FieldLabel} this
29749              */
29750             valid : true
29751         });
29752 };
29753
29754 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29755     
29756     tag: 'label',
29757     cls: '',
29758     html: '',
29759     target: '',
29760     allowBlank : true,
29761     invalidClass : 'has-warning',
29762     validClass : 'has-success',
29763     iconTooltip : 'This field is required',
29764     indicatorpos : 'left',
29765     
29766     getAutoCreate : function(){
29767         
29768         var cfg = {
29769             tag : this.tag,
29770             cls : 'roo-bootstrap-field-label ' + this.cls,
29771             for : this.target,
29772             cn : [
29773                 {
29774                     tag : 'i',
29775                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29776                     tooltip : this.iconTooltip
29777                 },
29778                 {
29779                     tag : 'span',
29780                     html : this.html
29781                 }
29782             ] 
29783         };
29784         
29785         if(this.indicatorpos == 'right'){
29786             var cfg = {
29787                 tag : this.tag,
29788                 cls : 'roo-bootstrap-field-label ' + this.cls,
29789                 for : this.target,
29790                 cn : [
29791                     {
29792                         tag : 'span',
29793                         html : this.html
29794                     },
29795                     {
29796                         tag : 'i',
29797                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29798                         tooltip : this.iconTooltip
29799                     }
29800                 ] 
29801             };
29802         }
29803         
29804         return cfg;
29805     },
29806     
29807     initEvents: function() 
29808     {
29809         Roo.bootstrap.Element.superclass.initEvents.call(this);
29810         
29811         this.indicator = this.indicatorEl();
29812         
29813         if(this.indicator){
29814             this.indicator.removeClass('visible');
29815             this.indicator.addClass('invisible');
29816         }
29817         
29818         Roo.bootstrap.FieldLabel.register(this);
29819     },
29820     
29821     indicatorEl : function()
29822     {
29823         var indicator = this.el.select('i.roo-required-indicator',true).first();
29824         
29825         if(!indicator){
29826             return false;
29827         }
29828         
29829         return indicator;
29830         
29831     },
29832     
29833     /**
29834      * Mark this field as valid
29835      */
29836     markValid : function()
29837     {
29838         if(this.indicator){
29839             this.indicator.removeClass('visible');
29840             this.indicator.addClass('invisible');
29841         }
29842         
29843         this.el.removeClass(this.invalidClass);
29844         
29845         this.el.addClass(this.validClass);
29846         
29847         this.fireEvent('valid', this);
29848     },
29849     
29850     /**
29851      * Mark this field as invalid
29852      * @param {String} msg The validation message
29853      */
29854     markInvalid : function(msg)
29855     {
29856         if(this.indicator){
29857             this.indicator.removeClass('invisible');
29858             this.indicator.addClass('visible');
29859         }
29860         
29861         this.el.removeClass(this.validClass);
29862         
29863         this.el.addClass(this.invalidClass);
29864         
29865         this.fireEvent('invalid', this, msg);
29866     }
29867     
29868    
29869 });
29870
29871 Roo.apply(Roo.bootstrap.FieldLabel, {
29872     
29873     groups: {},
29874     
29875      /**
29876     * register a FieldLabel Group
29877     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29878     */
29879     register : function(label)
29880     {
29881         if(this.groups.hasOwnProperty(label.target)){
29882             return;
29883         }
29884      
29885         this.groups[label.target] = label;
29886         
29887     },
29888     /**
29889     * fetch a FieldLabel Group based on the target
29890     * @param {string} target
29891     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29892     */
29893     get: function(target) {
29894         if (typeof(this.groups[target]) == 'undefined') {
29895             return false;
29896         }
29897         
29898         return this.groups[target] ;
29899     }
29900 });
29901
29902  
29903
29904  /*
29905  * - LGPL
29906  *
29907  * page DateSplitField.
29908  * 
29909  */
29910
29911
29912 /**
29913  * @class Roo.bootstrap.DateSplitField
29914  * @extends Roo.bootstrap.Component
29915  * Bootstrap DateSplitField class
29916  * @cfg {string} fieldLabel - the label associated
29917  * @cfg {Number} labelWidth set the width of label (0-12)
29918  * @cfg {String} labelAlign (top|left)
29919  * @cfg {Boolean} dayAllowBlank (true|false) default false
29920  * @cfg {Boolean} monthAllowBlank (true|false) default false
29921  * @cfg {Boolean} yearAllowBlank (true|false) default false
29922  * @cfg {string} dayPlaceholder 
29923  * @cfg {string} monthPlaceholder
29924  * @cfg {string} yearPlaceholder
29925  * @cfg {string} dayFormat default 'd'
29926  * @cfg {string} monthFormat default 'm'
29927  * @cfg {string} yearFormat default 'Y'
29928  * @cfg {Number} labellg set the width of label (1-12)
29929  * @cfg {Number} labelmd set the width of label (1-12)
29930  * @cfg {Number} labelsm set the width of label (1-12)
29931  * @cfg {Number} labelxs set the width of label (1-12)
29932
29933  *     
29934  * @constructor
29935  * Create a new DateSplitField
29936  * @param {Object} config The config object
29937  */
29938
29939 Roo.bootstrap.DateSplitField = function(config){
29940     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29941     
29942     this.addEvents({
29943         // raw events
29944          /**
29945          * @event years
29946          * getting the data of years
29947          * @param {Roo.bootstrap.DateSplitField} this
29948          * @param {Object} years
29949          */
29950         "years" : true,
29951         /**
29952          * @event days
29953          * getting the data of days
29954          * @param {Roo.bootstrap.DateSplitField} this
29955          * @param {Object} days
29956          */
29957         "days" : true,
29958         /**
29959          * @event invalid
29960          * Fires after the field has been marked as invalid.
29961          * @param {Roo.form.Field} this
29962          * @param {String} msg The validation message
29963          */
29964         invalid : true,
29965        /**
29966          * @event valid
29967          * Fires after the field has been validated with no errors.
29968          * @param {Roo.form.Field} this
29969          */
29970         valid : true
29971     });
29972 };
29973
29974 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29975     
29976     fieldLabel : '',
29977     labelAlign : 'top',
29978     labelWidth : 3,
29979     dayAllowBlank : false,
29980     monthAllowBlank : false,
29981     yearAllowBlank : false,
29982     dayPlaceholder : '',
29983     monthPlaceholder : '',
29984     yearPlaceholder : '',
29985     dayFormat : 'd',
29986     monthFormat : 'm',
29987     yearFormat : 'Y',
29988     isFormField : true,
29989     labellg : 0,
29990     labelmd : 0,
29991     labelsm : 0,
29992     labelxs : 0,
29993     
29994     getAutoCreate : function()
29995     {
29996         var cfg = {
29997             tag : 'div',
29998             cls : 'row roo-date-split-field-group',
29999             cn : [
30000                 {
30001                     tag : 'input',
30002                     type : 'hidden',
30003                     cls : 'form-hidden-field roo-date-split-field-group-value',
30004                     name : this.name
30005                 }
30006             ]
30007         };
30008         
30009         var labelCls = 'col-md-12';
30010         var contentCls = 'col-md-4';
30011         
30012         if(this.fieldLabel){
30013             
30014             var label = {
30015                 tag : 'div',
30016                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30017                 cn : [
30018                     {
30019                         tag : 'label',
30020                         html : this.fieldLabel
30021                     }
30022                 ]
30023             };
30024             
30025             if(this.labelAlign == 'left'){
30026             
30027                 if(this.labelWidth > 12){
30028                     label.style = "width: " + this.labelWidth + 'px';
30029                 }
30030
30031                 if(this.labelWidth < 13 && this.labelmd == 0){
30032                     this.labelmd = this.labelWidth;
30033                 }
30034
30035                 if(this.labellg > 0){
30036                     labelCls = ' col-lg-' + this.labellg;
30037                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30038                 }
30039
30040                 if(this.labelmd > 0){
30041                     labelCls = ' col-md-' + this.labelmd;
30042                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30043                 }
30044
30045                 if(this.labelsm > 0){
30046                     labelCls = ' col-sm-' + this.labelsm;
30047                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30048                 }
30049
30050                 if(this.labelxs > 0){
30051                     labelCls = ' col-xs-' + this.labelxs;
30052                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30053                 }
30054             }
30055             
30056             label.cls += ' ' + labelCls;
30057             
30058             cfg.cn.push(label);
30059         }
30060         
30061         Roo.each(['day', 'month', 'year'], function(t){
30062             cfg.cn.push({
30063                 tag : 'div',
30064                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30065             });
30066         }, this);
30067         
30068         return cfg;
30069     },
30070     
30071     inputEl: function ()
30072     {
30073         return this.el.select('.roo-date-split-field-group-value', true).first();
30074     },
30075     
30076     onRender : function(ct, position) 
30077     {
30078         var _this = this;
30079         
30080         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30081         
30082         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30083         
30084         this.dayField = new Roo.bootstrap.ComboBox({
30085             allowBlank : this.dayAllowBlank,
30086             alwaysQuery : true,
30087             displayField : 'value',
30088             editable : false,
30089             fieldLabel : '',
30090             forceSelection : true,
30091             mode : 'local',
30092             placeholder : this.dayPlaceholder,
30093             selectOnFocus : true,
30094             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30095             triggerAction : 'all',
30096             typeAhead : true,
30097             valueField : 'value',
30098             store : new Roo.data.SimpleStore({
30099                 data : (function() {    
30100                     var days = [];
30101                     _this.fireEvent('days', _this, days);
30102                     return days;
30103                 })(),
30104                 fields : [ 'value' ]
30105             }),
30106             listeners : {
30107                 select : function (_self, record, index)
30108                 {
30109                     _this.setValue(_this.getValue());
30110                 }
30111             }
30112         });
30113
30114         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30115         
30116         this.monthField = new Roo.bootstrap.MonthField({
30117             after : '<i class=\"fa fa-calendar\"></i>',
30118             allowBlank : this.monthAllowBlank,
30119             placeholder : this.monthPlaceholder,
30120             readOnly : true,
30121             listeners : {
30122                 render : function (_self)
30123                 {
30124                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30125                         e.preventDefault();
30126                         _self.focus();
30127                     });
30128                 },
30129                 select : function (_self, oldvalue, newvalue)
30130                 {
30131                     _this.setValue(_this.getValue());
30132                 }
30133             }
30134         });
30135         
30136         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30137         
30138         this.yearField = new Roo.bootstrap.ComboBox({
30139             allowBlank : this.yearAllowBlank,
30140             alwaysQuery : true,
30141             displayField : 'value',
30142             editable : false,
30143             fieldLabel : '',
30144             forceSelection : true,
30145             mode : 'local',
30146             placeholder : this.yearPlaceholder,
30147             selectOnFocus : true,
30148             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30149             triggerAction : 'all',
30150             typeAhead : true,
30151             valueField : 'value',
30152             store : new Roo.data.SimpleStore({
30153                 data : (function() {
30154                     var years = [];
30155                     _this.fireEvent('years', _this, years);
30156                     return years;
30157                 })(),
30158                 fields : [ 'value' ]
30159             }),
30160             listeners : {
30161                 select : function (_self, record, index)
30162                 {
30163                     _this.setValue(_this.getValue());
30164                 }
30165             }
30166         });
30167
30168         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30169     },
30170     
30171     setValue : function(v, format)
30172     {
30173         this.inputEl.dom.value = v;
30174         
30175         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30176         
30177         var d = Date.parseDate(v, f);
30178         
30179         if(!d){
30180             this.validate();
30181             return;
30182         }
30183         
30184         this.setDay(d.format(this.dayFormat));
30185         this.setMonth(d.format(this.monthFormat));
30186         this.setYear(d.format(this.yearFormat));
30187         
30188         this.validate();
30189         
30190         return;
30191     },
30192     
30193     setDay : function(v)
30194     {
30195         this.dayField.setValue(v);
30196         this.inputEl.dom.value = this.getValue();
30197         this.validate();
30198         return;
30199     },
30200     
30201     setMonth : function(v)
30202     {
30203         this.monthField.setValue(v, true);
30204         this.inputEl.dom.value = this.getValue();
30205         this.validate();
30206         return;
30207     },
30208     
30209     setYear : function(v)
30210     {
30211         this.yearField.setValue(v);
30212         this.inputEl.dom.value = this.getValue();
30213         this.validate();
30214         return;
30215     },
30216     
30217     getDay : function()
30218     {
30219         return this.dayField.getValue();
30220     },
30221     
30222     getMonth : function()
30223     {
30224         return this.monthField.getValue();
30225     },
30226     
30227     getYear : function()
30228     {
30229         return this.yearField.getValue();
30230     },
30231     
30232     getValue : function()
30233     {
30234         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30235         
30236         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30237         
30238         return date;
30239     },
30240     
30241     reset : function()
30242     {
30243         this.setDay('');
30244         this.setMonth('');
30245         this.setYear('');
30246         this.inputEl.dom.value = '';
30247         this.validate();
30248         return;
30249     },
30250     
30251     validate : function()
30252     {
30253         var d = this.dayField.validate();
30254         var m = this.monthField.validate();
30255         var y = this.yearField.validate();
30256         
30257         var valid = true;
30258         
30259         if(
30260                 (!this.dayAllowBlank && !d) ||
30261                 (!this.monthAllowBlank && !m) ||
30262                 (!this.yearAllowBlank && !y)
30263         ){
30264             valid = false;
30265         }
30266         
30267         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30268             return valid;
30269         }
30270         
30271         if(valid){
30272             this.markValid();
30273             return valid;
30274         }
30275         
30276         this.markInvalid();
30277         
30278         return valid;
30279     },
30280     
30281     markValid : function()
30282     {
30283         
30284         var label = this.el.select('label', true).first();
30285         var icon = this.el.select('i.fa-star', true).first();
30286
30287         if(label && icon){
30288             icon.remove();
30289         }
30290         
30291         this.fireEvent('valid', this);
30292     },
30293     
30294      /**
30295      * Mark this field as invalid
30296      * @param {String} msg The validation message
30297      */
30298     markInvalid : function(msg)
30299     {
30300         
30301         var label = this.el.select('label', true).first();
30302         var icon = this.el.select('i.fa-star', true).first();
30303
30304         if(label && !icon){
30305             this.el.select('.roo-date-split-field-label', true).createChild({
30306                 tag : 'i',
30307                 cls : 'text-danger fa fa-lg fa-star',
30308                 tooltip : 'This field is required',
30309                 style : 'margin-right:5px;'
30310             }, label, true);
30311         }
30312         
30313         this.fireEvent('invalid', this, msg);
30314     },
30315     
30316     clearInvalid : function()
30317     {
30318         var label = this.el.select('label', true).first();
30319         var icon = this.el.select('i.fa-star', true).first();
30320
30321         if(label && icon){
30322             icon.remove();
30323         }
30324         
30325         this.fireEvent('valid', this);
30326     },
30327     
30328     getName: function()
30329     {
30330         return this.name;
30331     }
30332     
30333 });
30334
30335  /**
30336  *
30337  * This is based on 
30338  * http://masonry.desandro.com
30339  *
30340  * The idea is to render all the bricks based on vertical width...
30341  *
30342  * The original code extends 'outlayer' - we might need to use that....
30343  * 
30344  */
30345
30346
30347 /**
30348  * @class Roo.bootstrap.LayoutMasonry
30349  * @extends Roo.bootstrap.Component
30350  * Bootstrap Layout Masonry class
30351  * 
30352  * @constructor
30353  * Create a new Element
30354  * @param {Object} config The config object
30355  */
30356
30357 Roo.bootstrap.LayoutMasonry = function(config){
30358     
30359     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30360     
30361     this.bricks = [];
30362     
30363     Roo.bootstrap.LayoutMasonry.register(this);
30364     
30365     this.addEvents({
30366         // raw events
30367         /**
30368          * @event layout
30369          * Fire after layout the items
30370          * @param {Roo.bootstrap.LayoutMasonry} this
30371          * @param {Roo.EventObject} e
30372          */
30373         "layout" : true
30374     });
30375     
30376 };
30377
30378 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30379     
30380     /**
30381      * @cfg {Boolean} isLayoutInstant = no animation?
30382      */   
30383     isLayoutInstant : false, // needed?
30384    
30385     /**
30386      * @cfg {Number} boxWidth  width of the columns
30387      */   
30388     boxWidth : 450,
30389     
30390       /**
30391      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30392      */   
30393     boxHeight : 0,
30394     
30395     /**
30396      * @cfg {Number} padWidth padding below box..
30397      */   
30398     padWidth : 10, 
30399     
30400     /**
30401      * @cfg {Number} gutter gutter width..
30402      */   
30403     gutter : 10,
30404     
30405      /**
30406      * @cfg {Number} maxCols maximum number of columns
30407      */   
30408     
30409     maxCols: 0,
30410     
30411     /**
30412      * @cfg {Boolean} isAutoInitial defalut true
30413      */   
30414     isAutoInitial : true, 
30415     
30416     containerWidth: 0,
30417     
30418     /**
30419      * @cfg {Boolean} isHorizontal defalut false
30420      */   
30421     isHorizontal : false, 
30422
30423     currentSize : null,
30424     
30425     tag: 'div',
30426     
30427     cls: '',
30428     
30429     bricks: null, //CompositeElement
30430     
30431     cols : 1,
30432     
30433     _isLayoutInited : false,
30434     
30435 //    isAlternative : false, // only use for vertical layout...
30436     
30437     /**
30438      * @cfg {Number} alternativePadWidth padding below box..
30439      */   
30440     alternativePadWidth : 50,
30441     
30442     selectedBrick : [],
30443     
30444     getAutoCreate : function(){
30445         
30446         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30447         
30448         var cfg = {
30449             tag: this.tag,
30450             cls: 'blog-masonary-wrapper ' + this.cls,
30451             cn : {
30452                 cls : 'mas-boxes masonary'
30453             }
30454         };
30455         
30456         return cfg;
30457     },
30458     
30459     getChildContainer: function( )
30460     {
30461         if (this.boxesEl) {
30462             return this.boxesEl;
30463         }
30464         
30465         this.boxesEl = this.el.select('.mas-boxes').first();
30466         
30467         return this.boxesEl;
30468     },
30469     
30470     
30471     initEvents : function()
30472     {
30473         var _this = this;
30474         
30475         if(this.isAutoInitial){
30476             Roo.log('hook children rendered');
30477             this.on('childrenrendered', function() {
30478                 Roo.log('children rendered');
30479                 _this.initial();
30480             } ,this);
30481         }
30482     },
30483     
30484     initial : function()
30485     {
30486         this.selectedBrick = [];
30487         
30488         this.currentSize = this.el.getBox(true);
30489         
30490         Roo.EventManager.onWindowResize(this.resize, this); 
30491
30492         if(!this.isAutoInitial){
30493             this.layout();
30494             return;
30495         }
30496         
30497         this.layout();
30498         
30499         return;
30500         //this.layout.defer(500,this);
30501         
30502     },
30503     
30504     resize : function()
30505     {
30506         var cs = this.el.getBox(true);
30507         
30508         if (
30509                 this.currentSize.width == cs.width && 
30510                 this.currentSize.x == cs.x && 
30511                 this.currentSize.height == cs.height && 
30512                 this.currentSize.y == cs.y 
30513         ) {
30514             Roo.log("no change in with or X or Y");
30515             return;
30516         }
30517         
30518         this.currentSize = cs;
30519         
30520         this.layout();
30521         
30522     },
30523     
30524     layout : function()
30525     {   
30526         this._resetLayout();
30527         
30528         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30529         
30530         this.layoutItems( isInstant );
30531       
30532         this._isLayoutInited = true;
30533         
30534         this.fireEvent('layout', this);
30535         
30536     },
30537     
30538     _resetLayout : function()
30539     {
30540         if(this.isHorizontal){
30541             this.horizontalMeasureColumns();
30542             return;
30543         }
30544         
30545         this.verticalMeasureColumns();
30546         
30547     },
30548     
30549     verticalMeasureColumns : function()
30550     {
30551         this.getContainerWidth();
30552         
30553 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30554 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30555 //            return;
30556 //        }
30557         
30558         var boxWidth = this.boxWidth + this.padWidth;
30559         
30560         if(this.containerWidth < this.boxWidth){
30561             boxWidth = this.containerWidth
30562         }
30563         
30564         var containerWidth = this.containerWidth;
30565         
30566         var cols = Math.floor(containerWidth / boxWidth);
30567         
30568         this.cols = Math.max( cols, 1 );
30569         
30570         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30571         
30572         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30573         
30574         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30575         
30576         this.colWidth = boxWidth + avail - this.padWidth;
30577         
30578         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30579         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30580     },
30581     
30582     horizontalMeasureColumns : function()
30583     {
30584         this.getContainerWidth();
30585         
30586         var boxWidth = this.boxWidth;
30587         
30588         if(this.containerWidth < boxWidth){
30589             boxWidth = this.containerWidth;
30590         }
30591         
30592         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30593         
30594         this.el.setHeight(boxWidth);
30595         
30596     },
30597     
30598     getContainerWidth : function()
30599     {
30600         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30601     },
30602     
30603     layoutItems : function( isInstant )
30604     {
30605         Roo.log(this.bricks);
30606         
30607         var items = Roo.apply([], this.bricks);
30608         
30609         if(this.isHorizontal){
30610             this._horizontalLayoutItems( items , isInstant );
30611             return;
30612         }
30613         
30614 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30615 //            this._verticalAlternativeLayoutItems( items , isInstant );
30616 //            return;
30617 //        }
30618         
30619         this._verticalLayoutItems( items , isInstant );
30620         
30621     },
30622     
30623     _verticalLayoutItems : function ( items , isInstant)
30624     {
30625         if ( !items || !items.length ) {
30626             return;
30627         }
30628         
30629         var standard = [
30630             ['xs', 'xs', 'xs', 'tall'],
30631             ['xs', 'xs', 'tall'],
30632             ['xs', 'xs', 'sm'],
30633             ['xs', 'xs', 'xs'],
30634             ['xs', 'tall'],
30635             ['xs', 'sm'],
30636             ['xs', 'xs'],
30637             ['xs'],
30638             
30639             ['sm', 'xs', 'xs'],
30640             ['sm', 'xs'],
30641             ['sm'],
30642             
30643             ['tall', 'xs', 'xs', 'xs'],
30644             ['tall', 'xs', 'xs'],
30645             ['tall', 'xs'],
30646             ['tall']
30647             
30648         ];
30649         
30650         var queue = [];
30651         
30652         var boxes = [];
30653         
30654         var box = [];
30655         
30656         Roo.each(items, function(item, k){
30657             
30658             switch (item.size) {
30659                 // these layouts take up a full box,
30660                 case 'md' :
30661                 case 'md-left' :
30662                 case 'md-right' :
30663                 case 'wide' :
30664                     
30665                     if(box.length){
30666                         boxes.push(box);
30667                         box = [];
30668                     }
30669                     
30670                     boxes.push([item]);
30671                     
30672                     break;
30673                     
30674                 case 'xs' :
30675                 case 'sm' :
30676                 case 'tall' :
30677                     
30678                     box.push(item);
30679                     
30680                     break;
30681                 default :
30682                     break;
30683                     
30684             }
30685             
30686         }, this);
30687         
30688         if(box.length){
30689             boxes.push(box);
30690             box = [];
30691         }
30692         
30693         var filterPattern = function(box, length)
30694         {
30695             if(!box.length){
30696                 return;
30697             }
30698             
30699             var match = false;
30700             
30701             var pattern = box.slice(0, length);
30702             
30703             var format = [];
30704             
30705             Roo.each(pattern, function(i){
30706                 format.push(i.size);
30707             }, this);
30708             
30709             Roo.each(standard, function(s){
30710                 
30711                 if(String(s) != String(format)){
30712                     return;
30713                 }
30714                 
30715                 match = true;
30716                 return false;
30717                 
30718             }, this);
30719             
30720             if(!match && length == 1){
30721                 return;
30722             }
30723             
30724             if(!match){
30725                 filterPattern(box, length - 1);
30726                 return;
30727             }
30728                 
30729             queue.push(pattern);
30730
30731             box = box.slice(length, box.length);
30732
30733             filterPattern(box, 4);
30734
30735             return;
30736             
30737         }
30738         
30739         Roo.each(boxes, function(box, k){
30740             
30741             if(!box.length){
30742                 return;
30743             }
30744             
30745             if(box.length == 1){
30746                 queue.push(box);
30747                 return;
30748             }
30749             
30750             filterPattern(box, 4);
30751             
30752         }, this);
30753         
30754         this._processVerticalLayoutQueue( queue, isInstant );
30755         
30756     },
30757     
30758 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30759 //    {
30760 //        if ( !items || !items.length ) {
30761 //            return;
30762 //        }
30763 //
30764 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30765 //        
30766 //    },
30767     
30768     _horizontalLayoutItems : function ( items , isInstant)
30769     {
30770         if ( !items || !items.length || items.length < 3) {
30771             return;
30772         }
30773         
30774         items.reverse();
30775         
30776         var eItems = items.slice(0, 3);
30777         
30778         items = items.slice(3, items.length);
30779         
30780         var standard = [
30781             ['xs', 'xs', 'xs', 'wide'],
30782             ['xs', 'xs', 'wide'],
30783             ['xs', 'xs', 'sm'],
30784             ['xs', 'xs', 'xs'],
30785             ['xs', 'wide'],
30786             ['xs', 'sm'],
30787             ['xs', 'xs'],
30788             ['xs'],
30789             
30790             ['sm', 'xs', 'xs'],
30791             ['sm', 'xs'],
30792             ['sm'],
30793             
30794             ['wide', 'xs', 'xs', 'xs'],
30795             ['wide', 'xs', 'xs'],
30796             ['wide', 'xs'],
30797             ['wide'],
30798             
30799             ['wide-thin']
30800         ];
30801         
30802         var queue = [];
30803         
30804         var boxes = [];
30805         
30806         var box = [];
30807         
30808         Roo.each(items, function(item, k){
30809             
30810             switch (item.size) {
30811                 case 'md' :
30812                 case 'md-left' :
30813                 case 'md-right' :
30814                 case 'tall' :
30815                     
30816                     if(box.length){
30817                         boxes.push(box);
30818                         box = [];
30819                     }
30820                     
30821                     boxes.push([item]);
30822                     
30823                     break;
30824                     
30825                 case 'xs' :
30826                 case 'sm' :
30827                 case 'wide' :
30828                 case 'wide-thin' :
30829                     
30830                     box.push(item);
30831                     
30832                     break;
30833                 default :
30834                     break;
30835                     
30836             }
30837             
30838         }, this);
30839         
30840         if(box.length){
30841             boxes.push(box);
30842             box = [];
30843         }
30844         
30845         var filterPattern = function(box, length)
30846         {
30847             if(!box.length){
30848                 return;
30849             }
30850             
30851             var match = false;
30852             
30853             var pattern = box.slice(0, length);
30854             
30855             var format = [];
30856             
30857             Roo.each(pattern, function(i){
30858                 format.push(i.size);
30859             }, this);
30860             
30861             Roo.each(standard, function(s){
30862                 
30863                 if(String(s) != String(format)){
30864                     return;
30865                 }
30866                 
30867                 match = true;
30868                 return false;
30869                 
30870             }, this);
30871             
30872             if(!match && length == 1){
30873                 return;
30874             }
30875             
30876             if(!match){
30877                 filterPattern(box, length - 1);
30878                 return;
30879             }
30880                 
30881             queue.push(pattern);
30882
30883             box = box.slice(length, box.length);
30884
30885             filterPattern(box, 4);
30886
30887             return;
30888             
30889         }
30890         
30891         Roo.each(boxes, function(box, k){
30892             
30893             if(!box.length){
30894                 return;
30895             }
30896             
30897             if(box.length == 1){
30898                 queue.push(box);
30899                 return;
30900             }
30901             
30902             filterPattern(box, 4);
30903             
30904         }, this);
30905         
30906         
30907         var prune = [];
30908         
30909         var pos = this.el.getBox(true);
30910         
30911         var minX = pos.x;
30912         
30913         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30914         
30915         var hit_end = false;
30916         
30917         Roo.each(queue, function(box){
30918             
30919             if(hit_end){
30920                 
30921                 Roo.each(box, function(b){
30922                 
30923                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30924                     b.el.hide();
30925
30926                 }, this);
30927
30928                 return;
30929             }
30930             
30931             var mx = 0;
30932             
30933             Roo.each(box, function(b){
30934                 
30935                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30936                 b.el.show();
30937
30938                 mx = Math.max(mx, b.x);
30939                 
30940             }, this);
30941             
30942             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30943             
30944             if(maxX < minX){
30945                 
30946                 Roo.each(box, function(b){
30947                 
30948                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30949                     b.el.hide();
30950                     
30951                 }, this);
30952                 
30953                 hit_end = true;
30954                 
30955                 return;
30956             }
30957             
30958             prune.push(box);
30959             
30960         }, this);
30961         
30962         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30963     },
30964     
30965     /** Sets position of item in DOM
30966     * @param {Element} item
30967     * @param {Number} x - horizontal position
30968     * @param {Number} y - vertical position
30969     * @param {Boolean} isInstant - disables transitions
30970     */
30971     _processVerticalLayoutQueue : function( queue, isInstant )
30972     {
30973         var pos = this.el.getBox(true);
30974         var x = pos.x;
30975         var y = pos.y;
30976         var maxY = [];
30977         
30978         for (var i = 0; i < this.cols; i++){
30979             maxY[i] = pos.y;
30980         }
30981         
30982         Roo.each(queue, function(box, k){
30983             
30984             var col = k % this.cols;
30985             
30986             Roo.each(box, function(b,kk){
30987                 
30988                 b.el.position('absolute');
30989                 
30990                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30991                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30992                 
30993                 if(b.size == 'md-left' || b.size == 'md-right'){
30994                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30995                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30996                 }
30997                 
30998                 b.el.setWidth(width);
30999                 b.el.setHeight(height);
31000                 // iframe?
31001                 b.el.select('iframe',true).setSize(width,height);
31002                 
31003             }, this);
31004             
31005             for (var i = 0; i < this.cols; i++){
31006                 
31007                 if(maxY[i] < maxY[col]){
31008                     col = i;
31009                     continue;
31010                 }
31011                 
31012                 col = Math.min(col, i);
31013                 
31014             }
31015             
31016             x = pos.x + col * (this.colWidth + this.padWidth);
31017             
31018             y = maxY[col];
31019             
31020             var positions = [];
31021             
31022             switch (box.length){
31023                 case 1 :
31024                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31025                     break;
31026                 case 2 :
31027                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31028                     break;
31029                 case 3 :
31030                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31031                     break;
31032                 case 4 :
31033                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31034                     break;
31035                 default :
31036                     break;
31037             }
31038             
31039             Roo.each(box, function(b,kk){
31040                 
31041                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31042                 
31043                 var sz = b.el.getSize();
31044                 
31045                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31046                 
31047             }, this);
31048             
31049         }, this);
31050         
31051         var mY = 0;
31052         
31053         for (var i = 0; i < this.cols; i++){
31054             mY = Math.max(mY, maxY[i]);
31055         }
31056         
31057         this.el.setHeight(mY - pos.y);
31058         
31059     },
31060     
31061 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31062 //    {
31063 //        var pos = this.el.getBox(true);
31064 //        var x = pos.x;
31065 //        var y = pos.y;
31066 //        var maxX = pos.right;
31067 //        
31068 //        var maxHeight = 0;
31069 //        
31070 //        Roo.each(items, function(item, k){
31071 //            
31072 //            var c = k % 2;
31073 //            
31074 //            item.el.position('absolute');
31075 //                
31076 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31077 //
31078 //            item.el.setWidth(width);
31079 //
31080 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31081 //
31082 //            item.el.setHeight(height);
31083 //            
31084 //            if(c == 0){
31085 //                item.el.setXY([x, y], isInstant ? false : true);
31086 //            } else {
31087 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31088 //            }
31089 //            
31090 //            y = y + height + this.alternativePadWidth;
31091 //            
31092 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31093 //            
31094 //        }, this);
31095 //        
31096 //        this.el.setHeight(maxHeight);
31097 //        
31098 //    },
31099     
31100     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31101     {
31102         var pos = this.el.getBox(true);
31103         
31104         var minX = pos.x;
31105         var minY = pos.y;
31106         
31107         var maxX = pos.right;
31108         
31109         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31110         
31111         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31112         
31113         Roo.each(queue, function(box, k){
31114             
31115             Roo.each(box, function(b, kk){
31116                 
31117                 b.el.position('absolute');
31118                 
31119                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31120                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31121                 
31122                 if(b.size == 'md-left' || b.size == 'md-right'){
31123                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31124                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31125                 }
31126                 
31127                 b.el.setWidth(width);
31128                 b.el.setHeight(height);
31129                 
31130             }, this);
31131             
31132             if(!box.length){
31133                 return;
31134             }
31135             
31136             var positions = [];
31137             
31138             switch (box.length){
31139                 case 1 :
31140                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31141                     break;
31142                 case 2 :
31143                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31144                     break;
31145                 case 3 :
31146                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31147                     break;
31148                 case 4 :
31149                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31150                     break;
31151                 default :
31152                     break;
31153             }
31154             
31155             Roo.each(box, function(b,kk){
31156                 
31157                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31158                 
31159                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31160                 
31161             }, this);
31162             
31163         }, this);
31164         
31165     },
31166     
31167     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31168     {
31169         Roo.each(eItems, function(b,k){
31170             
31171             b.size = (k == 0) ? 'sm' : 'xs';
31172             b.x = (k == 0) ? 2 : 1;
31173             b.y = (k == 0) ? 2 : 1;
31174             
31175             b.el.position('absolute');
31176             
31177             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31178                 
31179             b.el.setWidth(width);
31180             
31181             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31182             
31183             b.el.setHeight(height);
31184             
31185         }, this);
31186
31187         var positions = [];
31188         
31189         positions.push({
31190             x : maxX - this.unitWidth * 2 - this.gutter,
31191             y : minY
31192         });
31193         
31194         positions.push({
31195             x : maxX - this.unitWidth,
31196             y : minY + (this.unitWidth + this.gutter) * 2
31197         });
31198         
31199         positions.push({
31200             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31201             y : minY
31202         });
31203         
31204         Roo.each(eItems, function(b,k){
31205             
31206             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31207
31208         }, this);
31209         
31210     },
31211     
31212     getVerticalOneBoxColPositions : function(x, y, box)
31213     {
31214         var pos = [];
31215         
31216         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31217         
31218         if(box[0].size == 'md-left'){
31219             rand = 0;
31220         }
31221         
31222         if(box[0].size == 'md-right'){
31223             rand = 1;
31224         }
31225         
31226         pos.push({
31227             x : x + (this.unitWidth + this.gutter) * rand,
31228             y : y
31229         });
31230         
31231         return pos;
31232     },
31233     
31234     getVerticalTwoBoxColPositions : function(x, y, box)
31235     {
31236         var pos = [];
31237         
31238         if(box[0].size == 'xs'){
31239             
31240             pos.push({
31241                 x : x,
31242                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31243             });
31244
31245             pos.push({
31246                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31247                 y : y
31248             });
31249             
31250             return pos;
31251             
31252         }
31253         
31254         pos.push({
31255             x : x,
31256             y : y
31257         });
31258
31259         pos.push({
31260             x : x + (this.unitWidth + this.gutter) * 2,
31261             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31262         });
31263         
31264         return pos;
31265         
31266     },
31267     
31268     getVerticalThreeBoxColPositions : function(x, y, box)
31269     {
31270         var pos = [];
31271         
31272         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31273             
31274             pos.push({
31275                 x : x,
31276                 y : y
31277             });
31278
31279             pos.push({
31280                 x : x + (this.unitWidth + this.gutter) * 1,
31281                 y : y
31282             });
31283             
31284             pos.push({
31285                 x : x + (this.unitWidth + this.gutter) * 2,
31286                 y : y
31287             });
31288             
31289             return pos;
31290             
31291         }
31292         
31293         if(box[0].size == 'xs' && box[1].size == 'xs'){
31294             
31295             pos.push({
31296                 x : x,
31297                 y : y
31298             });
31299
31300             pos.push({
31301                 x : x,
31302                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31303             });
31304             
31305             pos.push({
31306                 x : x + (this.unitWidth + this.gutter) * 1,
31307                 y : y
31308             });
31309             
31310             return pos;
31311             
31312         }
31313         
31314         pos.push({
31315             x : x,
31316             y : y
31317         });
31318
31319         pos.push({
31320             x : x + (this.unitWidth + this.gutter) * 2,
31321             y : y
31322         });
31323
31324         pos.push({
31325             x : x + (this.unitWidth + this.gutter) * 2,
31326             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31327         });
31328             
31329         return pos;
31330         
31331     },
31332     
31333     getVerticalFourBoxColPositions : function(x, y, box)
31334     {
31335         var pos = [];
31336         
31337         if(box[0].size == 'xs'){
31338             
31339             pos.push({
31340                 x : x,
31341                 y : y
31342             });
31343
31344             pos.push({
31345                 x : x,
31346                 y : y + (this.unitHeight + this.gutter) * 1
31347             });
31348             
31349             pos.push({
31350                 x : x,
31351                 y : y + (this.unitHeight + this.gutter) * 2
31352             });
31353             
31354             pos.push({
31355                 x : x + (this.unitWidth + this.gutter) * 1,
31356                 y : y
31357             });
31358             
31359             return pos;
31360             
31361         }
31362         
31363         pos.push({
31364             x : x,
31365             y : y
31366         });
31367
31368         pos.push({
31369             x : x + (this.unitWidth + this.gutter) * 2,
31370             y : y
31371         });
31372
31373         pos.push({
31374             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31375             y : y + (this.unitHeight + this.gutter) * 1
31376         });
31377
31378         pos.push({
31379             x : x + (this.unitWidth + this.gutter) * 2,
31380             y : y + (this.unitWidth + this.gutter) * 2
31381         });
31382
31383         return pos;
31384         
31385     },
31386     
31387     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31388     {
31389         var pos = [];
31390         
31391         if(box[0].size == 'md-left'){
31392             pos.push({
31393                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31394                 y : minY
31395             });
31396             
31397             return pos;
31398         }
31399         
31400         if(box[0].size == 'md-right'){
31401             pos.push({
31402                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31403                 y : minY + (this.unitWidth + this.gutter) * 1
31404             });
31405             
31406             return pos;
31407         }
31408         
31409         var rand = Math.floor(Math.random() * (4 - box[0].y));
31410         
31411         pos.push({
31412             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31413             y : minY + (this.unitWidth + this.gutter) * rand
31414         });
31415         
31416         return pos;
31417         
31418     },
31419     
31420     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31421     {
31422         var pos = [];
31423         
31424         if(box[0].size == 'xs'){
31425             
31426             pos.push({
31427                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31428                 y : minY
31429             });
31430
31431             pos.push({
31432                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31433                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31434             });
31435             
31436             return pos;
31437             
31438         }
31439         
31440         pos.push({
31441             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31442             y : minY
31443         });
31444
31445         pos.push({
31446             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31447             y : minY + (this.unitWidth + this.gutter) * 2
31448         });
31449         
31450         return pos;
31451         
31452     },
31453     
31454     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31455     {
31456         var pos = [];
31457         
31458         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31459             
31460             pos.push({
31461                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31462                 y : minY
31463             });
31464
31465             pos.push({
31466                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31467                 y : minY + (this.unitWidth + this.gutter) * 1
31468             });
31469             
31470             pos.push({
31471                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31472                 y : minY + (this.unitWidth + this.gutter) * 2
31473             });
31474             
31475             return pos;
31476             
31477         }
31478         
31479         if(box[0].size == 'xs' && box[1].size == 'xs'){
31480             
31481             pos.push({
31482                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31483                 y : minY
31484             });
31485
31486             pos.push({
31487                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31488                 y : minY
31489             });
31490             
31491             pos.push({
31492                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31493                 y : minY + (this.unitWidth + this.gutter) * 1
31494             });
31495             
31496             return pos;
31497             
31498         }
31499         
31500         pos.push({
31501             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31502             y : minY
31503         });
31504
31505         pos.push({
31506             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31507             y : minY + (this.unitWidth + this.gutter) * 2
31508         });
31509
31510         pos.push({
31511             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31512             y : minY + (this.unitWidth + this.gutter) * 2
31513         });
31514             
31515         return pos;
31516         
31517     },
31518     
31519     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31520     {
31521         var pos = [];
31522         
31523         if(box[0].size == 'xs'){
31524             
31525             pos.push({
31526                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31527                 y : minY
31528             });
31529
31530             pos.push({
31531                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31532                 y : minY
31533             });
31534             
31535             pos.push({
31536                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31537                 y : minY
31538             });
31539             
31540             pos.push({
31541                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31542                 y : minY + (this.unitWidth + this.gutter) * 1
31543             });
31544             
31545             return pos;
31546             
31547         }
31548         
31549         pos.push({
31550             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31551             y : minY
31552         });
31553         
31554         pos.push({
31555             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31556             y : minY + (this.unitWidth + this.gutter) * 2
31557         });
31558         
31559         pos.push({
31560             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31561             y : minY + (this.unitWidth + this.gutter) * 2
31562         });
31563         
31564         pos.push({
31565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31566             y : minY + (this.unitWidth + this.gutter) * 2
31567         });
31568
31569         return pos;
31570         
31571     },
31572     
31573     /**
31574     * remove a Masonry Brick
31575     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31576     */
31577     removeBrick : function(brick_id)
31578     {
31579         if (!brick_id) {
31580             return;
31581         }
31582         
31583         for (var i = 0; i<this.bricks.length; i++) {
31584             if (this.bricks[i].id == brick_id) {
31585                 this.bricks.splice(i,1);
31586                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31587                 this.initial();
31588             }
31589         }
31590     },
31591     
31592     /**
31593     * adds a Masonry Brick
31594     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31595     */
31596     addBrick : function(cfg)
31597     {
31598         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31599         //this.register(cn);
31600         cn.parentId = this.id;
31601         cn.onRender(this.el, null);
31602         return cn;
31603     },
31604     
31605     /**
31606     * register a Masonry Brick
31607     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31608     */
31609     
31610     register : function(brick)
31611     {
31612         this.bricks.push(brick);
31613         brick.masonryId = this.id;
31614     },
31615     
31616     /**
31617     * clear all the Masonry Brick
31618     */
31619     clearAll : function()
31620     {
31621         this.bricks = [];
31622         //this.getChildContainer().dom.innerHTML = "";
31623         this.el.dom.innerHTML = '';
31624     },
31625     
31626     getSelected : function()
31627     {
31628         if (!this.selectedBrick) {
31629             return false;
31630         }
31631         
31632         return this.selectedBrick;
31633     }
31634 });
31635
31636 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31637     
31638     groups: {},
31639      /**
31640     * register a Masonry Layout
31641     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31642     */
31643     
31644     register : function(layout)
31645     {
31646         this.groups[layout.id] = layout;
31647     },
31648     /**
31649     * fetch a  Masonry Layout based on the masonry layout ID
31650     * @param {string} the masonry layout to add
31651     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31652     */
31653     
31654     get: function(layout_id) {
31655         if (typeof(this.groups[layout_id]) == 'undefined') {
31656             return false;
31657         }
31658         return this.groups[layout_id] ;
31659     }
31660     
31661     
31662     
31663 });
31664
31665  
31666
31667  /**
31668  *
31669  * This is based on 
31670  * http://masonry.desandro.com
31671  *
31672  * The idea is to render all the bricks based on vertical width...
31673  *
31674  * The original code extends 'outlayer' - we might need to use that....
31675  * 
31676  */
31677
31678
31679 /**
31680  * @class Roo.bootstrap.LayoutMasonryAuto
31681  * @extends Roo.bootstrap.Component
31682  * Bootstrap Layout Masonry class
31683  * 
31684  * @constructor
31685  * Create a new Element
31686  * @param {Object} config The config object
31687  */
31688
31689 Roo.bootstrap.LayoutMasonryAuto = function(config){
31690     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31691 };
31692
31693 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31694     
31695       /**
31696      * @cfg {Boolean} isFitWidth  - resize the width..
31697      */   
31698     isFitWidth : false,  // options..
31699     /**
31700      * @cfg {Boolean} isOriginLeft = left align?
31701      */   
31702     isOriginLeft : true,
31703     /**
31704      * @cfg {Boolean} isOriginTop = top align?
31705      */   
31706     isOriginTop : false,
31707     /**
31708      * @cfg {Boolean} isLayoutInstant = no animation?
31709      */   
31710     isLayoutInstant : false, // needed?
31711     /**
31712      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31713      */   
31714     isResizingContainer : true,
31715     /**
31716      * @cfg {Number} columnWidth  width of the columns 
31717      */   
31718     
31719     columnWidth : 0,
31720     
31721     /**
31722      * @cfg {Number} maxCols maximum number of columns
31723      */   
31724     
31725     maxCols: 0,
31726     /**
31727      * @cfg {Number} padHeight padding below box..
31728      */   
31729     
31730     padHeight : 10, 
31731     
31732     /**
31733      * @cfg {Boolean} isAutoInitial defalut true
31734      */   
31735     
31736     isAutoInitial : true, 
31737     
31738     // private?
31739     gutter : 0,
31740     
31741     containerWidth: 0,
31742     initialColumnWidth : 0,
31743     currentSize : null,
31744     
31745     colYs : null, // array.
31746     maxY : 0,
31747     padWidth: 10,
31748     
31749     
31750     tag: 'div',
31751     cls: '',
31752     bricks: null, //CompositeElement
31753     cols : 0, // array?
31754     // element : null, // wrapped now this.el
31755     _isLayoutInited : null, 
31756     
31757     
31758     getAutoCreate : function(){
31759         
31760         var cfg = {
31761             tag: this.tag,
31762             cls: 'blog-masonary-wrapper ' + this.cls,
31763             cn : {
31764                 cls : 'mas-boxes masonary'
31765             }
31766         };
31767         
31768         return cfg;
31769     },
31770     
31771     getChildContainer: function( )
31772     {
31773         if (this.boxesEl) {
31774             return this.boxesEl;
31775         }
31776         
31777         this.boxesEl = this.el.select('.mas-boxes').first();
31778         
31779         return this.boxesEl;
31780     },
31781     
31782     
31783     initEvents : function()
31784     {
31785         var _this = this;
31786         
31787         if(this.isAutoInitial){
31788             Roo.log('hook children rendered');
31789             this.on('childrenrendered', function() {
31790                 Roo.log('children rendered');
31791                 _this.initial();
31792             } ,this);
31793         }
31794         
31795     },
31796     
31797     initial : function()
31798     {
31799         this.reloadItems();
31800
31801         this.currentSize = this.el.getBox(true);
31802
31803         /// was window resize... - let's see if this works..
31804         Roo.EventManager.onWindowResize(this.resize, this); 
31805
31806         if(!this.isAutoInitial){
31807             this.layout();
31808             return;
31809         }
31810         
31811         this.layout.defer(500,this);
31812     },
31813     
31814     reloadItems: function()
31815     {
31816         this.bricks = this.el.select('.masonry-brick', true);
31817         
31818         this.bricks.each(function(b) {
31819             //Roo.log(b.getSize());
31820             if (!b.attr('originalwidth')) {
31821                 b.attr('originalwidth',  b.getSize().width);
31822             }
31823             
31824         });
31825         
31826         Roo.log(this.bricks.elements.length);
31827     },
31828     
31829     resize : function()
31830     {
31831         Roo.log('resize');
31832         var cs = this.el.getBox(true);
31833         
31834         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31835             Roo.log("no change in with or X");
31836             return;
31837         }
31838         this.currentSize = cs;
31839         this.layout();
31840     },
31841     
31842     layout : function()
31843     {
31844          Roo.log('layout');
31845         this._resetLayout();
31846         //this._manageStamps();
31847       
31848         // don't animate first layout
31849         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31850         this.layoutItems( isInstant );
31851       
31852         // flag for initalized
31853         this._isLayoutInited = true;
31854     },
31855     
31856     layoutItems : function( isInstant )
31857     {
31858         //var items = this._getItemsForLayout( this.items );
31859         // original code supports filtering layout items.. we just ignore it..
31860         
31861         this._layoutItems( this.bricks , isInstant );
31862       
31863         this._postLayout();
31864     },
31865     _layoutItems : function ( items , isInstant)
31866     {
31867        //this.fireEvent( 'layout', this, items );
31868     
31869
31870         if ( !items || !items.elements.length ) {
31871           // no items, emit event with empty array
31872             return;
31873         }
31874
31875         var queue = [];
31876         items.each(function(item) {
31877             Roo.log("layout item");
31878             Roo.log(item);
31879             // get x/y object from method
31880             var position = this._getItemLayoutPosition( item );
31881             // enqueue
31882             position.item = item;
31883             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31884             queue.push( position );
31885         }, this);
31886       
31887         this._processLayoutQueue( queue );
31888     },
31889     /** Sets position of item in DOM
31890     * @param {Element} item
31891     * @param {Number} x - horizontal position
31892     * @param {Number} y - vertical position
31893     * @param {Boolean} isInstant - disables transitions
31894     */
31895     _processLayoutQueue : function( queue )
31896     {
31897         for ( var i=0, len = queue.length; i < len; i++ ) {
31898             var obj = queue[i];
31899             obj.item.position('absolute');
31900             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31901         }
31902     },
31903       
31904     
31905     /**
31906     * Any logic you want to do after each layout,
31907     * i.e. size the container
31908     */
31909     _postLayout : function()
31910     {
31911         this.resizeContainer();
31912     },
31913     
31914     resizeContainer : function()
31915     {
31916         if ( !this.isResizingContainer ) {
31917             return;
31918         }
31919         var size = this._getContainerSize();
31920         if ( size ) {
31921             this.el.setSize(size.width,size.height);
31922             this.boxesEl.setSize(size.width,size.height);
31923         }
31924     },
31925     
31926     
31927     
31928     _resetLayout : function()
31929     {
31930         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31931         this.colWidth = this.el.getWidth();
31932         //this.gutter = this.el.getWidth(); 
31933         
31934         this.measureColumns();
31935
31936         // reset column Y
31937         var i = this.cols;
31938         this.colYs = [];
31939         while (i--) {
31940             this.colYs.push( 0 );
31941         }
31942     
31943         this.maxY = 0;
31944     },
31945
31946     measureColumns : function()
31947     {
31948         this.getContainerWidth();
31949       // if columnWidth is 0, default to outerWidth of first item
31950         if ( !this.columnWidth ) {
31951             var firstItem = this.bricks.first();
31952             Roo.log(firstItem);
31953             this.columnWidth  = this.containerWidth;
31954             if (firstItem && firstItem.attr('originalwidth') ) {
31955                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31956             }
31957             // columnWidth fall back to item of first element
31958             Roo.log("set column width?");
31959                         this.initialColumnWidth = this.columnWidth  ;
31960
31961             // if first elem has no width, default to size of container
31962             
31963         }
31964         
31965         
31966         if (this.initialColumnWidth) {
31967             this.columnWidth = this.initialColumnWidth;
31968         }
31969         
31970         
31971             
31972         // column width is fixed at the top - however if container width get's smaller we should
31973         // reduce it...
31974         
31975         // this bit calcs how man columns..
31976             
31977         var columnWidth = this.columnWidth += this.gutter;
31978       
31979         // calculate columns
31980         var containerWidth = this.containerWidth + this.gutter;
31981         
31982         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31983         // fix rounding errors, typically with gutters
31984         var excess = columnWidth - containerWidth % columnWidth;
31985         
31986         
31987         // if overshoot is less than a pixel, round up, otherwise floor it
31988         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31989         cols = Math[ mathMethod ]( cols );
31990         this.cols = Math.max( cols, 1 );
31991         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31992         
31993          // padding positioning..
31994         var totalColWidth = this.cols * this.columnWidth;
31995         var padavail = this.containerWidth - totalColWidth;
31996         // so for 2 columns - we need 3 'pads'
31997         
31998         var padNeeded = (1+this.cols) * this.padWidth;
31999         
32000         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32001         
32002         this.columnWidth += padExtra
32003         //this.padWidth = Math.floor(padavail /  ( this.cols));
32004         
32005         // adjust colum width so that padding is fixed??
32006         
32007         // we have 3 columns ... total = width * 3
32008         // we have X left over... that should be used by 
32009         
32010         //if (this.expandC) {
32011             
32012         //}
32013         
32014         
32015         
32016     },
32017     
32018     getContainerWidth : function()
32019     {
32020        /* // container is parent if fit width
32021         var container = this.isFitWidth ? this.element.parentNode : this.element;
32022         // check that this.size and size are there
32023         // IE8 triggers resize on body size change, so they might not be
32024         
32025         var size = getSize( container );  //FIXME
32026         this.containerWidth = size && size.innerWidth; //FIXME
32027         */
32028          
32029         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32030         
32031     },
32032     
32033     _getItemLayoutPosition : function( item )  // what is item?
32034     {
32035         // we resize the item to our columnWidth..
32036       
32037         item.setWidth(this.columnWidth);
32038         item.autoBoxAdjust  = false;
32039         
32040         var sz = item.getSize();
32041  
32042         // how many columns does this brick span
32043         var remainder = this.containerWidth % this.columnWidth;
32044         
32045         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32046         // round if off by 1 pixel, otherwise use ceil
32047         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32048         colSpan = Math.min( colSpan, this.cols );
32049         
32050         // normally this should be '1' as we dont' currently allow multi width columns..
32051         
32052         var colGroup = this._getColGroup( colSpan );
32053         // get the minimum Y value from the columns
32054         var minimumY = Math.min.apply( Math, colGroup );
32055         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32056         
32057         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32058          
32059         // position the brick
32060         var position = {
32061             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32062             y: this.currentSize.y + minimumY + this.padHeight
32063         };
32064         
32065         Roo.log(position);
32066         // apply setHeight to necessary columns
32067         var setHeight = minimumY + sz.height + this.padHeight;
32068         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32069         
32070         var setSpan = this.cols + 1 - colGroup.length;
32071         for ( var i = 0; i < setSpan; i++ ) {
32072           this.colYs[ shortColIndex + i ] = setHeight ;
32073         }
32074       
32075         return position;
32076     },
32077     
32078     /**
32079      * @param {Number} colSpan - number of columns the element spans
32080      * @returns {Array} colGroup
32081      */
32082     _getColGroup : function( colSpan )
32083     {
32084         if ( colSpan < 2 ) {
32085           // if brick spans only one column, use all the column Ys
32086           return this.colYs;
32087         }
32088       
32089         var colGroup = [];
32090         // how many different places could this brick fit horizontally
32091         var groupCount = this.cols + 1 - colSpan;
32092         // for each group potential horizontal position
32093         for ( var i = 0; i < groupCount; i++ ) {
32094           // make an array of colY values for that one group
32095           var groupColYs = this.colYs.slice( i, i + colSpan );
32096           // and get the max value of the array
32097           colGroup[i] = Math.max.apply( Math, groupColYs );
32098         }
32099         return colGroup;
32100     },
32101     /*
32102     _manageStamp : function( stamp )
32103     {
32104         var stampSize =  stamp.getSize();
32105         var offset = stamp.getBox();
32106         // get the columns that this stamp affects
32107         var firstX = this.isOriginLeft ? offset.x : offset.right;
32108         var lastX = firstX + stampSize.width;
32109         var firstCol = Math.floor( firstX / this.columnWidth );
32110         firstCol = Math.max( 0, firstCol );
32111         
32112         var lastCol = Math.floor( lastX / this.columnWidth );
32113         // lastCol should not go over if multiple of columnWidth #425
32114         lastCol -= lastX % this.columnWidth ? 0 : 1;
32115         lastCol = Math.min( this.cols - 1, lastCol );
32116         
32117         // set colYs to bottom of the stamp
32118         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32119             stampSize.height;
32120             
32121         for ( var i = firstCol; i <= lastCol; i++ ) {
32122           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32123         }
32124     },
32125     */
32126     
32127     _getContainerSize : function()
32128     {
32129         this.maxY = Math.max.apply( Math, this.colYs );
32130         var size = {
32131             height: this.maxY
32132         };
32133       
32134         if ( this.isFitWidth ) {
32135             size.width = this._getContainerFitWidth();
32136         }
32137       
32138         return size;
32139     },
32140     
32141     _getContainerFitWidth : function()
32142     {
32143         var unusedCols = 0;
32144         // count unused columns
32145         var i = this.cols;
32146         while ( --i ) {
32147           if ( this.colYs[i] !== 0 ) {
32148             break;
32149           }
32150           unusedCols++;
32151         }
32152         // fit container to columns that have been used
32153         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32154     },
32155     
32156     needsResizeLayout : function()
32157     {
32158         var previousWidth = this.containerWidth;
32159         this.getContainerWidth();
32160         return previousWidth !== this.containerWidth;
32161     }
32162  
32163 });
32164
32165  
32166
32167  /*
32168  * - LGPL
32169  *
32170  * element
32171  * 
32172  */
32173
32174 /**
32175  * @class Roo.bootstrap.MasonryBrick
32176  * @extends Roo.bootstrap.Component
32177  * Bootstrap MasonryBrick class
32178  * 
32179  * @constructor
32180  * Create a new MasonryBrick
32181  * @param {Object} config The config object
32182  */
32183
32184 Roo.bootstrap.MasonryBrick = function(config){
32185     
32186     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32187     
32188     Roo.bootstrap.MasonryBrick.register(this);
32189     
32190     this.addEvents({
32191         // raw events
32192         /**
32193          * @event click
32194          * When a MasonryBrick is clcik
32195          * @param {Roo.bootstrap.MasonryBrick} this
32196          * @param {Roo.EventObject} e
32197          */
32198         "click" : true
32199     });
32200 };
32201
32202 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32203     
32204     /**
32205      * @cfg {String} title
32206      */   
32207     title : '',
32208     /**
32209      * @cfg {String} html
32210      */   
32211     html : '',
32212     /**
32213      * @cfg {String} bgimage
32214      */   
32215     bgimage : '',
32216     /**
32217      * @cfg {String} videourl
32218      */   
32219     videourl : '',
32220     /**
32221      * @cfg {String} cls
32222      */   
32223     cls : '',
32224     /**
32225      * @cfg {String} href
32226      */   
32227     href : '',
32228     /**
32229      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32230      */   
32231     size : 'xs',
32232     
32233     /**
32234      * @cfg {String} placetitle (center|bottom)
32235      */   
32236     placetitle : '',
32237     
32238     /**
32239      * @cfg {Boolean} isFitContainer defalut true
32240      */   
32241     isFitContainer : true, 
32242     
32243     /**
32244      * @cfg {Boolean} preventDefault defalut false
32245      */   
32246     preventDefault : false, 
32247     
32248     /**
32249      * @cfg {Boolean} inverse defalut false
32250      */   
32251     maskInverse : false, 
32252     
32253     getAutoCreate : function()
32254     {
32255         if(!this.isFitContainer){
32256             return this.getSplitAutoCreate();
32257         }
32258         
32259         var cls = 'masonry-brick masonry-brick-full';
32260         
32261         if(this.href.length){
32262             cls += ' masonry-brick-link';
32263         }
32264         
32265         if(this.bgimage.length){
32266             cls += ' masonry-brick-image';
32267         }
32268         
32269         if(this.maskInverse){
32270             cls += ' mask-inverse';
32271         }
32272         
32273         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32274             cls += ' enable-mask';
32275         }
32276         
32277         if(this.size){
32278             cls += ' masonry-' + this.size + '-brick';
32279         }
32280         
32281         if(this.placetitle.length){
32282             
32283             switch (this.placetitle) {
32284                 case 'center' :
32285                     cls += ' masonry-center-title';
32286                     break;
32287                 case 'bottom' :
32288                     cls += ' masonry-bottom-title';
32289                     break;
32290                 default:
32291                     break;
32292             }
32293             
32294         } else {
32295             if(!this.html.length && !this.bgimage.length){
32296                 cls += ' masonry-center-title';
32297             }
32298
32299             if(!this.html.length && this.bgimage.length){
32300                 cls += ' masonry-bottom-title';
32301             }
32302         }
32303         
32304         if(this.cls){
32305             cls += ' ' + this.cls;
32306         }
32307         
32308         var cfg = {
32309             tag: (this.href.length) ? 'a' : 'div',
32310             cls: cls,
32311             cn: [
32312                 {
32313                     tag: 'div',
32314                     cls: 'masonry-brick-mask'
32315                 },
32316                 {
32317                     tag: 'div',
32318                     cls: 'masonry-brick-paragraph',
32319                     cn: []
32320                 }
32321             ]
32322         };
32323         
32324         if(this.href.length){
32325             cfg.href = this.href;
32326         }
32327         
32328         var cn = cfg.cn[1].cn;
32329         
32330         if(this.title.length){
32331             cn.push({
32332                 tag: 'h4',
32333                 cls: 'masonry-brick-title',
32334                 html: this.title
32335             });
32336         }
32337         
32338         if(this.html.length){
32339             cn.push({
32340                 tag: 'p',
32341                 cls: 'masonry-brick-text',
32342                 html: this.html
32343             });
32344         }
32345         
32346         if (!this.title.length && !this.html.length) {
32347             cfg.cn[1].cls += ' hide';
32348         }
32349         
32350         if(this.bgimage.length){
32351             cfg.cn.push({
32352                 tag: 'img',
32353                 cls: 'masonry-brick-image-view',
32354                 src: this.bgimage
32355             });
32356         }
32357         
32358         if(this.videourl.length){
32359             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32360             // youtube support only?
32361             cfg.cn.push({
32362                 tag: 'iframe',
32363                 cls: 'masonry-brick-image-view',
32364                 src: vurl,
32365                 frameborder : 0,
32366                 allowfullscreen : true
32367             });
32368         }
32369         
32370         return cfg;
32371         
32372     },
32373     
32374     getSplitAutoCreate : function()
32375     {
32376         var cls = 'masonry-brick masonry-brick-split';
32377         
32378         if(this.href.length){
32379             cls += ' masonry-brick-link';
32380         }
32381         
32382         if(this.bgimage.length){
32383             cls += ' masonry-brick-image';
32384         }
32385         
32386         if(this.size){
32387             cls += ' masonry-' + this.size + '-brick';
32388         }
32389         
32390         switch (this.placetitle) {
32391             case 'center' :
32392                 cls += ' masonry-center-title';
32393                 break;
32394             case 'bottom' :
32395                 cls += ' masonry-bottom-title';
32396                 break;
32397             default:
32398                 if(!this.bgimage.length){
32399                     cls += ' masonry-center-title';
32400                 }
32401
32402                 if(this.bgimage.length){
32403                     cls += ' masonry-bottom-title';
32404                 }
32405                 break;
32406         }
32407         
32408         if(this.cls){
32409             cls += ' ' + this.cls;
32410         }
32411         
32412         var cfg = {
32413             tag: (this.href.length) ? 'a' : 'div',
32414             cls: cls,
32415             cn: [
32416                 {
32417                     tag: 'div',
32418                     cls: 'masonry-brick-split-head',
32419                     cn: [
32420                         {
32421                             tag: 'div',
32422                             cls: 'masonry-brick-paragraph',
32423                             cn: []
32424                         }
32425                     ]
32426                 },
32427                 {
32428                     tag: 'div',
32429                     cls: 'masonry-brick-split-body',
32430                     cn: []
32431                 }
32432             ]
32433         };
32434         
32435         if(this.href.length){
32436             cfg.href = this.href;
32437         }
32438         
32439         if(this.title.length){
32440             cfg.cn[0].cn[0].cn.push({
32441                 tag: 'h4',
32442                 cls: 'masonry-brick-title',
32443                 html: this.title
32444             });
32445         }
32446         
32447         if(this.html.length){
32448             cfg.cn[1].cn.push({
32449                 tag: 'p',
32450                 cls: 'masonry-brick-text',
32451                 html: this.html
32452             });
32453         }
32454
32455         if(this.bgimage.length){
32456             cfg.cn[0].cn.push({
32457                 tag: 'img',
32458                 cls: 'masonry-brick-image-view',
32459                 src: this.bgimage
32460             });
32461         }
32462         
32463         if(this.videourl.length){
32464             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32465             // youtube support only?
32466             cfg.cn[0].cn.cn.push({
32467                 tag: 'iframe',
32468                 cls: 'masonry-brick-image-view',
32469                 src: vurl,
32470                 frameborder : 0,
32471                 allowfullscreen : true
32472             });
32473         }
32474         
32475         return cfg;
32476     },
32477     
32478     initEvents: function() 
32479     {
32480         switch (this.size) {
32481             case 'xs' :
32482                 this.x = 1;
32483                 this.y = 1;
32484                 break;
32485             case 'sm' :
32486                 this.x = 2;
32487                 this.y = 2;
32488                 break;
32489             case 'md' :
32490             case 'md-left' :
32491             case 'md-right' :
32492                 this.x = 3;
32493                 this.y = 3;
32494                 break;
32495             case 'tall' :
32496                 this.x = 2;
32497                 this.y = 3;
32498                 break;
32499             case 'wide' :
32500                 this.x = 3;
32501                 this.y = 2;
32502                 break;
32503             case 'wide-thin' :
32504                 this.x = 3;
32505                 this.y = 1;
32506                 break;
32507                         
32508             default :
32509                 break;
32510         }
32511         
32512         if(Roo.isTouch){
32513             this.el.on('touchstart', this.onTouchStart, this);
32514             this.el.on('touchmove', this.onTouchMove, this);
32515             this.el.on('touchend', this.onTouchEnd, this);
32516             this.el.on('contextmenu', this.onContextMenu, this);
32517         } else {
32518             this.el.on('mouseenter'  ,this.enter, this);
32519             this.el.on('mouseleave', this.leave, this);
32520             this.el.on('click', this.onClick, this);
32521         }
32522         
32523         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32524             this.parent().bricks.push(this);   
32525         }
32526         
32527     },
32528     
32529     onClick: function(e, el)
32530     {
32531         var time = this.endTimer - this.startTimer;
32532         // Roo.log(e.preventDefault());
32533         if(Roo.isTouch){
32534             if(time > 1000){
32535                 e.preventDefault();
32536                 return;
32537             }
32538         }
32539         
32540         if(!this.preventDefault){
32541             return;
32542         }
32543         
32544         e.preventDefault();
32545         
32546         if (this.activcClass != '') {
32547             this.selectBrick();
32548         }
32549         
32550         this.fireEvent('click', this);
32551     },
32552     
32553     enter: function(e, el)
32554     {
32555         e.preventDefault();
32556         
32557         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32558             return;
32559         }
32560         
32561         if(this.bgimage.length && this.html.length){
32562             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32563         }
32564     },
32565     
32566     leave: function(e, el)
32567     {
32568         e.preventDefault();
32569         
32570         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32571             return;
32572         }
32573         
32574         if(this.bgimage.length && this.html.length){
32575             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32576         }
32577     },
32578     
32579     onTouchStart: function(e, el)
32580     {
32581 //        e.preventDefault();
32582         
32583         this.touchmoved = false;
32584         
32585         if(!this.isFitContainer){
32586             return;
32587         }
32588         
32589         if(!this.bgimage.length || !this.html.length){
32590             return;
32591         }
32592         
32593         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32594         
32595         this.timer = new Date().getTime();
32596         
32597     },
32598     
32599     onTouchMove: function(e, el)
32600     {
32601         this.touchmoved = true;
32602     },
32603     
32604     onContextMenu : function(e,el)
32605     {
32606         e.preventDefault();
32607         e.stopPropagation();
32608         return false;
32609     },
32610     
32611     onTouchEnd: function(e, el)
32612     {
32613 //        e.preventDefault();
32614         
32615         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32616         
32617             this.leave(e,el);
32618             
32619             return;
32620         }
32621         
32622         if(!this.bgimage.length || !this.html.length){
32623             
32624             if(this.href.length){
32625                 window.location.href = this.href;
32626             }
32627             
32628             return;
32629         }
32630         
32631         if(!this.isFitContainer){
32632             return;
32633         }
32634         
32635         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32636         
32637         window.location.href = this.href;
32638     },
32639     
32640     //selection on single brick only
32641     selectBrick : function() {
32642         
32643         if (!this.parentId) {
32644             return;
32645         }
32646         
32647         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32648         var index = m.selectedBrick.indexOf(this.id);
32649         
32650         if ( index > -1) {
32651             m.selectedBrick.splice(index,1);
32652             this.el.removeClass(this.activeClass);
32653             return;
32654         }
32655         
32656         for(var i = 0; i < m.selectedBrick.length; i++) {
32657             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32658             b.el.removeClass(b.activeClass);
32659         }
32660         
32661         m.selectedBrick = [];
32662         
32663         m.selectedBrick.push(this.id);
32664         this.el.addClass(this.activeClass);
32665         return;
32666     }
32667     
32668 });
32669
32670 Roo.apply(Roo.bootstrap.MasonryBrick, {
32671     
32672     //groups: {},
32673     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32674      /**
32675     * register a Masonry Brick
32676     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32677     */
32678     
32679     register : function(brick)
32680     {
32681         //this.groups[brick.id] = brick;
32682         this.groups.add(brick.id, brick);
32683     },
32684     /**
32685     * fetch a  masonry brick based on the masonry brick ID
32686     * @param {string} the masonry brick to add
32687     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32688     */
32689     
32690     get: function(brick_id) 
32691     {
32692         // if (typeof(this.groups[brick_id]) == 'undefined') {
32693         //     return false;
32694         // }
32695         // return this.groups[brick_id] ;
32696         
32697         if(this.groups.key(brick_id)) {
32698             return this.groups.key(brick_id);
32699         }
32700         
32701         return false;
32702     }
32703     
32704     
32705     
32706 });
32707
32708  /*
32709  * - LGPL
32710  *
32711  * element
32712  * 
32713  */
32714
32715 /**
32716  * @class Roo.bootstrap.Brick
32717  * @extends Roo.bootstrap.Component
32718  * Bootstrap Brick class
32719  * 
32720  * @constructor
32721  * Create a new Brick
32722  * @param {Object} config The config object
32723  */
32724
32725 Roo.bootstrap.Brick = function(config){
32726     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32727     
32728     this.addEvents({
32729         // raw events
32730         /**
32731          * @event click
32732          * When a Brick is click
32733          * @param {Roo.bootstrap.Brick} this
32734          * @param {Roo.EventObject} e
32735          */
32736         "click" : true
32737     });
32738 };
32739
32740 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32741     
32742     /**
32743      * @cfg {String} title
32744      */   
32745     title : '',
32746     /**
32747      * @cfg {String} html
32748      */   
32749     html : '',
32750     /**
32751      * @cfg {String} bgimage
32752      */   
32753     bgimage : '',
32754     /**
32755      * @cfg {String} cls
32756      */   
32757     cls : '',
32758     /**
32759      * @cfg {String} href
32760      */   
32761     href : '',
32762     /**
32763      * @cfg {String} video
32764      */   
32765     video : '',
32766     /**
32767      * @cfg {Boolean} square
32768      */   
32769     square : true,
32770     
32771     getAutoCreate : function()
32772     {
32773         var cls = 'roo-brick';
32774         
32775         if(this.href.length){
32776             cls += ' roo-brick-link';
32777         }
32778         
32779         if(this.bgimage.length){
32780             cls += ' roo-brick-image';
32781         }
32782         
32783         if(!this.html.length && !this.bgimage.length){
32784             cls += ' roo-brick-center-title';
32785         }
32786         
32787         if(!this.html.length && this.bgimage.length){
32788             cls += ' roo-brick-bottom-title';
32789         }
32790         
32791         if(this.cls){
32792             cls += ' ' + this.cls;
32793         }
32794         
32795         var cfg = {
32796             tag: (this.href.length) ? 'a' : 'div',
32797             cls: cls,
32798             cn: [
32799                 {
32800                     tag: 'div',
32801                     cls: 'roo-brick-paragraph',
32802                     cn: []
32803                 }
32804             ]
32805         };
32806         
32807         if(this.href.length){
32808             cfg.href = this.href;
32809         }
32810         
32811         var cn = cfg.cn[0].cn;
32812         
32813         if(this.title.length){
32814             cn.push({
32815                 tag: 'h4',
32816                 cls: 'roo-brick-title',
32817                 html: this.title
32818             });
32819         }
32820         
32821         if(this.html.length){
32822             cn.push({
32823                 tag: 'p',
32824                 cls: 'roo-brick-text',
32825                 html: this.html
32826             });
32827         } else {
32828             cn.cls += ' hide';
32829         }
32830         
32831         if(this.bgimage.length){
32832             cfg.cn.push({
32833                 tag: 'img',
32834                 cls: 'roo-brick-image-view',
32835                 src: this.bgimage
32836             });
32837         }
32838         
32839         return cfg;
32840     },
32841     
32842     initEvents: function() 
32843     {
32844         if(this.title.length || this.html.length){
32845             this.el.on('mouseenter'  ,this.enter, this);
32846             this.el.on('mouseleave', this.leave, this);
32847         }
32848         
32849         Roo.EventManager.onWindowResize(this.resize, this); 
32850         
32851         if(this.bgimage.length){
32852             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32853             this.imageEl.on('load', this.onImageLoad, this);
32854             return;
32855         }
32856         
32857         this.resize();
32858     },
32859     
32860     onImageLoad : function()
32861     {
32862         this.resize();
32863     },
32864     
32865     resize : function()
32866     {
32867         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32868         
32869         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32870         
32871         if(this.bgimage.length){
32872             var image = this.el.select('.roo-brick-image-view', true).first();
32873             
32874             image.setWidth(paragraph.getWidth());
32875             
32876             if(this.square){
32877                 image.setHeight(paragraph.getWidth());
32878             }
32879             
32880             this.el.setHeight(image.getHeight());
32881             paragraph.setHeight(image.getHeight());
32882             
32883         }
32884         
32885     },
32886     
32887     enter: function(e, el)
32888     {
32889         e.preventDefault();
32890         
32891         if(this.bgimage.length){
32892             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32893             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32894         }
32895     },
32896     
32897     leave: function(e, el)
32898     {
32899         e.preventDefault();
32900         
32901         if(this.bgimage.length){
32902             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32903             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32904         }
32905     }
32906     
32907 });
32908
32909  
32910
32911  /*
32912  * - LGPL
32913  *
32914  * Input
32915  * 
32916  */
32917
32918 /**
32919  * @class Roo.bootstrap.NumberField
32920  * @extends Roo.bootstrap.Input
32921  * Bootstrap NumberField class
32922  * 
32923  * 
32924  * 
32925  * 
32926  * @constructor
32927  * Create a new NumberField
32928  * @param {Object} config The config object
32929  */
32930
32931 Roo.bootstrap.NumberField = function(config){
32932     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32933 };
32934
32935 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32936     
32937     /**
32938      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32939      */
32940     allowDecimals : true,
32941     /**
32942      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32943      */
32944     decimalSeparator : ".",
32945     /**
32946      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32947      */
32948     decimalPrecision : 2,
32949     /**
32950      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32951      */
32952     allowNegative : true,
32953     /**
32954      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32955      */
32956     minValue : Number.NEGATIVE_INFINITY,
32957     /**
32958      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32959      */
32960     maxValue : Number.MAX_VALUE,
32961     /**
32962      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32963      */
32964     minText : "The minimum value for this field is {0}",
32965     /**
32966      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32967      */
32968     maxText : "The maximum value for this field is {0}",
32969     /**
32970      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32971      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32972      */
32973     nanText : "{0} is not a valid number",
32974     /**
32975      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32976      */
32977     castInt : true,
32978
32979     // private
32980     initEvents : function()
32981     {   
32982         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32983         
32984         var allowed = "0123456789";
32985         
32986         if(this.allowDecimals){
32987             allowed += this.decimalSeparator;
32988         }
32989         
32990         if(this.allowNegative){
32991             allowed += "-";
32992         }
32993         
32994         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32995         
32996         var keyPress = function(e){
32997             
32998             var k = e.getKey();
32999             
33000             var c = e.getCharCode();
33001             
33002             if(
33003                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33004                     allowed.indexOf(String.fromCharCode(c)) === -1
33005             ){
33006                 e.stopEvent();
33007                 return;
33008             }
33009             
33010             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33011                 return;
33012             }
33013             
33014             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33015                 e.stopEvent();
33016             }
33017         };
33018         
33019         this.el.on("keypress", keyPress, this);
33020     },
33021     
33022     validateValue : function(value)
33023     {
33024         
33025         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33026             return false;
33027         }
33028         
33029         var num = this.parseValue(value);
33030         
33031         if(isNaN(num)){
33032             this.markInvalid(String.format(this.nanText, value));
33033             return false;
33034         }
33035         
33036         if(num < this.minValue){
33037             this.markInvalid(String.format(this.minText, this.minValue));
33038             return false;
33039         }
33040         
33041         if(num > this.maxValue){
33042             this.markInvalid(String.format(this.maxText, this.maxValue));
33043             return false;
33044         }
33045         
33046         return true;
33047     },
33048
33049     getValue : function()
33050     {
33051         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33052     },
33053
33054     parseValue : function(value)
33055     {
33056         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33057         return isNaN(value) ? '' : value;
33058     },
33059
33060     fixPrecision : function(value)
33061     {
33062         var nan = isNaN(value);
33063         
33064         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33065             return nan ? '' : value;
33066         }
33067         return parseFloat(value).toFixed(this.decimalPrecision);
33068     },
33069
33070     setValue : function(v)
33071     {
33072         v = this.fixPrecision(v);
33073         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33074     },
33075
33076     decimalPrecisionFcn : function(v)
33077     {
33078         return Math.floor(v);
33079     },
33080
33081     beforeBlur : function()
33082     {
33083         if(!this.castInt){
33084             return;
33085         }
33086         
33087         var v = this.parseValue(this.getRawValue());
33088         if(v){
33089             this.setValue(v);
33090         }
33091     }
33092     
33093 });
33094
33095  
33096
33097 /*
33098 * Licence: LGPL
33099 */
33100
33101 /**
33102  * @class Roo.bootstrap.DocumentSlider
33103  * @extends Roo.bootstrap.Component
33104  * Bootstrap DocumentSlider class
33105  * 
33106  * @constructor
33107  * Create a new DocumentViewer
33108  * @param {Object} config The config object
33109  */
33110
33111 Roo.bootstrap.DocumentSlider = function(config){
33112     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33113     
33114     this.files = [];
33115     
33116     this.addEvents({
33117         /**
33118          * @event initial
33119          * Fire after initEvent
33120          * @param {Roo.bootstrap.DocumentSlider} this
33121          */
33122         "initial" : true,
33123         /**
33124          * @event update
33125          * Fire after update
33126          * @param {Roo.bootstrap.DocumentSlider} this
33127          */
33128         "update" : true,
33129         /**
33130          * @event click
33131          * Fire after click
33132          * @param {Roo.bootstrap.DocumentSlider} this
33133          */
33134         "click" : true
33135     });
33136 };
33137
33138 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33139     
33140     files : false,
33141     
33142     indicator : 0,
33143     
33144     getAutoCreate : function()
33145     {
33146         var cfg = {
33147             tag : 'div',
33148             cls : 'roo-document-slider',
33149             cn : [
33150                 {
33151                     tag : 'div',
33152                     cls : 'roo-document-slider-header',
33153                     cn : [
33154                         {
33155                             tag : 'div',
33156                             cls : 'roo-document-slider-header-title'
33157                         }
33158                     ]
33159                 },
33160                 {
33161                     tag : 'div',
33162                     cls : 'roo-document-slider-body',
33163                     cn : [
33164                         {
33165                             tag : 'div',
33166                             cls : 'roo-document-slider-prev',
33167                             cn : [
33168                                 {
33169                                     tag : 'i',
33170                                     cls : 'fa fa-chevron-left'
33171                                 }
33172                             ]
33173                         },
33174                         {
33175                             tag : 'div',
33176                             cls : 'roo-document-slider-thumb',
33177                             cn : [
33178                                 {
33179                                     tag : 'img',
33180                                     cls : 'roo-document-slider-image'
33181                                 }
33182                             ]
33183                         },
33184                         {
33185                             tag : 'div',
33186                             cls : 'roo-document-slider-next',
33187                             cn : [
33188                                 {
33189                                     tag : 'i',
33190                                     cls : 'fa fa-chevron-right'
33191                                 }
33192                             ]
33193                         }
33194                     ]
33195                 }
33196             ]
33197         };
33198         
33199         return cfg;
33200     },
33201     
33202     initEvents : function()
33203     {
33204         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33205         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33206         
33207         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33208         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33209         
33210         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33211         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33212         
33213         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33214         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33215         
33216         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33217         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33218         
33219         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33220         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33221         
33222         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33223         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33224         
33225         this.thumbEl.on('click', this.onClick, this);
33226         
33227         this.prevIndicator.on('click', this.prev, this);
33228         
33229         this.nextIndicator.on('click', this.next, this);
33230         
33231     },
33232     
33233     initial : function()
33234     {
33235         if(this.files.length){
33236             this.indicator = 1;
33237             this.update()
33238         }
33239         
33240         this.fireEvent('initial', this);
33241     },
33242     
33243     update : function()
33244     {
33245         this.imageEl.attr('src', this.files[this.indicator - 1]);
33246         
33247         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33248         
33249         this.prevIndicator.show();
33250         
33251         if(this.indicator == 1){
33252             this.prevIndicator.hide();
33253         }
33254         
33255         this.nextIndicator.show();
33256         
33257         if(this.indicator == this.files.length){
33258             this.nextIndicator.hide();
33259         }
33260         
33261         this.thumbEl.scrollTo('top');
33262         
33263         this.fireEvent('update', this);
33264     },
33265     
33266     onClick : function(e)
33267     {
33268         e.preventDefault();
33269         
33270         this.fireEvent('click', this);
33271     },
33272     
33273     prev : function(e)
33274     {
33275         e.preventDefault();
33276         
33277         this.indicator = Math.max(1, this.indicator - 1);
33278         
33279         this.update();
33280     },
33281     
33282     next : function(e)
33283     {
33284         e.preventDefault();
33285         
33286         this.indicator = Math.min(this.files.length, this.indicator + 1);
33287         
33288         this.update();
33289     }
33290 });
33291 /*
33292  * - LGPL
33293  *
33294  * RadioSet
33295  *
33296  *
33297  */
33298
33299 /**
33300  * @class Roo.bootstrap.RadioSet
33301  * @extends Roo.bootstrap.Input
33302  * Bootstrap RadioSet class
33303  * @cfg {String} indicatorpos (left|right) default left
33304  * @cfg {Boolean} inline (true|false) inline the element (default true)
33305  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33306  * @constructor
33307  * Create a new RadioSet
33308  * @param {Object} config The config object
33309  */
33310
33311 Roo.bootstrap.RadioSet = function(config){
33312     
33313     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33314     
33315     this.radioes = [];
33316     
33317     Roo.bootstrap.RadioSet.register(this);
33318     
33319     this.addEvents({
33320         /**
33321         * @event check
33322         * Fires when the element is checked or unchecked.
33323         * @param {Roo.bootstrap.RadioSet} this This radio
33324         * @param {Roo.bootstrap.Radio} item The checked item
33325         */
33326        check : true
33327     });
33328     
33329 };
33330
33331 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33332
33333     radioes : false,
33334     
33335     inline : true,
33336     
33337     weight : '',
33338     
33339     indicatorpos : 'left',
33340     
33341     getAutoCreate : function()
33342     {
33343         var label = {
33344             tag : 'label',
33345             cls : 'roo-radio-set-label',
33346             cn : [
33347                 {
33348                     tag : 'span',
33349                     html : this.fieldLabel
33350                 }
33351             ]
33352         };
33353         
33354         if(this.indicatorpos == 'left'){
33355             label.cn.unshift({
33356                 tag : 'i',
33357                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33358                 tooltip : 'This field is required'
33359             });
33360         } else {
33361             label.cn.push({
33362                 tag : 'i',
33363                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33364                 tooltip : 'This field is required'
33365             });
33366         }
33367         
33368         var items = {
33369             tag : 'div',
33370             cls : 'roo-radio-set-items'
33371         };
33372         
33373         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33374         
33375         if (align === 'left' && this.fieldLabel.length) {
33376             
33377             items = {
33378                 cls : "roo-radio-set-right", 
33379                 cn: [
33380                     items
33381                 ]
33382             };
33383             
33384             if(this.labelWidth > 12){
33385                 label.style = "width: " + this.labelWidth + 'px';
33386             }
33387             
33388             if(this.labelWidth < 13 && this.labelmd == 0){
33389                 this.labelmd = this.labelWidth;
33390             }
33391             
33392             if(this.labellg > 0){
33393                 label.cls += ' col-lg-' + this.labellg;
33394                 items.cls += ' col-lg-' + (12 - this.labellg);
33395             }
33396             
33397             if(this.labelmd > 0){
33398                 label.cls += ' col-md-' + this.labelmd;
33399                 items.cls += ' col-md-' + (12 - this.labelmd);
33400             }
33401             
33402             if(this.labelsm > 0){
33403                 label.cls += ' col-sm-' + this.labelsm;
33404                 items.cls += ' col-sm-' + (12 - this.labelsm);
33405             }
33406             
33407             if(this.labelxs > 0){
33408                 label.cls += ' col-xs-' + this.labelxs;
33409                 items.cls += ' col-xs-' + (12 - this.labelxs);
33410             }
33411         }
33412         
33413         var cfg = {
33414             tag : 'div',
33415             cls : 'roo-radio-set',
33416             cn : [
33417                 {
33418                     tag : 'input',
33419                     cls : 'roo-radio-set-input',
33420                     type : 'hidden',
33421                     name : this.name,
33422                     value : this.value ? this.value :  ''
33423                 },
33424                 label,
33425                 items
33426             ]
33427         };
33428         
33429         if(this.weight.length){
33430             cfg.cls += ' roo-radio-' + this.weight;
33431         }
33432         
33433         if(this.inline) {
33434             cfg.cls += ' roo-radio-set-inline';
33435         }
33436         
33437         var settings=this;
33438         ['xs','sm','md','lg'].map(function(size){
33439             if (settings[size]) {
33440                 cfg.cls += ' col-' + size + '-' + settings[size];
33441             }
33442         });
33443         
33444         return cfg;
33445         
33446     },
33447
33448     initEvents : function()
33449     {
33450         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33451         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33452         
33453         if(!this.fieldLabel.length){
33454             this.labelEl.hide();
33455         }
33456         
33457         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33458         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33459         
33460         this.indicatorEl().addClass('invisible');
33461         
33462         this.originalValue = this.getValue();
33463         
33464     },
33465     
33466     inputEl: function ()
33467     {
33468         return this.el.select('.roo-radio-set-input', true).first();
33469     },
33470     
33471     getChildContainer : function()
33472     {
33473         return this.itemsEl;
33474     },
33475     
33476     register : function(item)
33477     {
33478         this.radioes.push(item);
33479         
33480     },
33481     
33482     validate : function()
33483     {   
33484         var valid = false;
33485         
33486         Roo.each(this.radioes, function(i){
33487             if(!i.checked){
33488                 return;
33489             }
33490             
33491             valid = true;
33492             return false;
33493         });
33494         
33495         if(this.allowBlank) {
33496             return true;
33497         }
33498         
33499         if(this.disabled || valid){
33500             this.markValid();
33501             return true;
33502         }
33503         
33504         this.markInvalid();
33505         return false;
33506         
33507     },
33508     
33509     markValid : function()
33510     {
33511         if(this.labelEl.isVisible(true)){
33512             this.indicatorEl().removeClass('visible');
33513             this.indicatorEl().addClass('invisible');
33514         }
33515         
33516         this.el.removeClass([this.invalidClass, this.validClass]);
33517         this.el.addClass(this.validClass);
33518         
33519         this.fireEvent('valid', this);
33520     },
33521     
33522     markInvalid : function(msg)
33523     {
33524         if(this.allowBlank || this.disabled){
33525             return;
33526         }
33527         
33528         if(this.labelEl.isVisible(true)){
33529             this.indicatorEl().removeClass('invisible');
33530             this.indicatorEl().addClass('visible');
33531         }
33532         
33533         this.el.removeClass([this.invalidClass, this.validClass]);
33534         this.el.addClass(this.invalidClass);
33535         
33536         this.fireEvent('invalid', this, msg);
33537         
33538     },
33539     
33540     setValue : function(v, suppressEvent)
33541     {   
33542         if(this.value === v){
33543             return;
33544         }
33545         
33546         this.value = v;
33547         
33548         if(this.rendered){
33549             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33550         }
33551         
33552         Roo.each(this.radioes, function(i){
33553             
33554             i.checked = false;
33555             i.el.removeClass('checked');
33556             
33557             if(i.value === v || i.value.toString() === v.toString()){
33558                 i.checked = true;
33559                 i.el.addClass('checked');
33560                 
33561                 if(suppressEvent !== true){
33562                     this.fireEvent('check', this, i);
33563                 }
33564             }
33565             
33566         }, this);
33567         
33568         this.validate();
33569     },
33570     
33571     clearInvalid : function(){
33572         
33573         if(!this.el || this.preventMark){
33574             return;
33575         }
33576         
33577         this.el.removeClass([this.invalidClass]);
33578         
33579         this.fireEvent('valid', this);
33580     }
33581     
33582 });
33583
33584 Roo.apply(Roo.bootstrap.RadioSet, {
33585     
33586     groups: {},
33587     
33588     register : function(set)
33589     {
33590         this.groups[set.name] = set;
33591     },
33592     
33593     get: function(name) 
33594     {
33595         if (typeof(this.groups[name]) == 'undefined') {
33596             return false;
33597         }
33598         
33599         return this.groups[name] ;
33600     }
33601     
33602 });
33603 /*
33604  * Based on:
33605  * Ext JS Library 1.1.1
33606  * Copyright(c) 2006-2007, Ext JS, LLC.
33607  *
33608  * Originally Released Under LGPL - original licence link has changed is not relivant.
33609  *
33610  * Fork - LGPL
33611  * <script type="text/javascript">
33612  */
33613
33614
33615 /**
33616  * @class Roo.bootstrap.SplitBar
33617  * @extends Roo.util.Observable
33618  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33619  * <br><br>
33620  * Usage:
33621  * <pre><code>
33622 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33623                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33624 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33625 split.minSize = 100;
33626 split.maxSize = 600;
33627 split.animate = true;
33628 split.on('moved', splitterMoved);
33629 </code></pre>
33630  * @constructor
33631  * Create a new SplitBar
33632  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33633  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33634  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33635  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33636                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33637                         position of the SplitBar).
33638  */
33639 Roo.bootstrap.SplitBar = function(cfg){
33640     
33641     /** @private */
33642     
33643     //{
33644     //  dragElement : elm
33645     //  resizingElement: el,
33646         // optional..
33647     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33648     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33649         // existingProxy ???
33650     //}
33651     
33652     this.el = Roo.get(cfg.dragElement, true);
33653     this.el.dom.unselectable = "on";
33654     /** @private */
33655     this.resizingEl = Roo.get(cfg.resizingElement, true);
33656
33657     /**
33658      * @private
33659      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33660      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33661      * @type Number
33662      */
33663     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33664     
33665     /**
33666      * The minimum size of the resizing element. (Defaults to 0)
33667      * @type Number
33668      */
33669     this.minSize = 0;
33670     
33671     /**
33672      * The maximum size of the resizing element. (Defaults to 2000)
33673      * @type Number
33674      */
33675     this.maxSize = 2000;
33676     
33677     /**
33678      * Whether to animate the transition to the new size
33679      * @type Boolean
33680      */
33681     this.animate = false;
33682     
33683     /**
33684      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33685      * @type Boolean
33686      */
33687     this.useShim = false;
33688     
33689     /** @private */
33690     this.shim = null;
33691     
33692     if(!cfg.existingProxy){
33693         /** @private */
33694         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33695     }else{
33696         this.proxy = Roo.get(cfg.existingProxy).dom;
33697     }
33698     /** @private */
33699     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33700     
33701     /** @private */
33702     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33703     
33704     /** @private */
33705     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33706     
33707     /** @private */
33708     this.dragSpecs = {};
33709     
33710     /**
33711      * @private The adapter to use to positon and resize elements
33712      */
33713     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33714     this.adapter.init(this);
33715     
33716     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33717         /** @private */
33718         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33719         this.el.addClass("roo-splitbar-h");
33720     }else{
33721         /** @private */
33722         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33723         this.el.addClass("roo-splitbar-v");
33724     }
33725     
33726     this.addEvents({
33727         /**
33728          * @event resize
33729          * Fires when the splitter is moved (alias for {@link #event-moved})
33730          * @param {Roo.bootstrap.SplitBar} this
33731          * @param {Number} newSize the new width or height
33732          */
33733         "resize" : true,
33734         /**
33735          * @event moved
33736          * Fires when the splitter is moved
33737          * @param {Roo.bootstrap.SplitBar} this
33738          * @param {Number} newSize the new width or height
33739          */
33740         "moved" : true,
33741         /**
33742          * @event beforeresize
33743          * Fires before the splitter is dragged
33744          * @param {Roo.bootstrap.SplitBar} this
33745          */
33746         "beforeresize" : true,
33747
33748         "beforeapply" : true
33749     });
33750
33751     Roo.util.Observable.call(this);
33752 };
33753
33754 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33755     onStartProxyDrag : function(x, y){
33756         this.fireEvent("beforeresize", this);
33757         if(!this.overlay){
33758             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33759             o.unselectable();
33760             o.enableDisplayMode("block");
33761             // all splitbars share the same overlay
33762             Roo.bootstrap.SplitBar.prototype.overlay = o;
33763         }
33764         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33765         this.overlay.show();
33766         Roo.get(this.proxy).setDisplayed("block");
33767         var size = this.adapter.getElementSize(this);
33768         this.activeMinSize = this.getMinimumSize();;
33769         this.activeMaxSize = this.getMaximumSize();;
33770         var c1 = size - this.activeMinSize;
33771         var c2 = Math.max(this.activeMaxSize - size, 0);
33772         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33773             this.dd.resetConstraints();
33774             this.dd.setXConstraint(
33775                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33776                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33777             );
33778             this.dd.setYConstraint(0, 0);
33779         }else{
33780             this.dd.resetConstraints();
33781             this.dd.setXConstraint(0, 0);
33782             this.dd.setYConstraint(
33783                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33784                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33785             );
33786          }
33787         this.dragSpecs.startSize = size;
33788         this.dragSpecs.startPoint = [x, y];
33789         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33790     },
33791     
33792     /** 
33793      * @private Called after the drag operation by the DDProxy
33794      */
33795     onEndProxyDrag : function(e){
33796         Roo.get(this.proxy).setDisplayed(false);
33797         var endPoint = Roo.lib.Event.getXY(e);
33798         if(this.overlay){
33799             this.overlay.hide();
33800         }
33801         var newSize;
33802         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33803             newSize = this.dragSpecs.startSize + 
33804                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33805                     endPoint[0] - this.dragSpecs.startPoint[0] :
33806                     this.dragSpecs.startPoint[0] - endPoint[0]
33807                 );
33808         }else{
33809             newSize = this.dragSpecs.startSize + 
33810                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33811                     endPoint[1] - this.dragSpecs.startPoint[1] :
33812                     this.dragSpecs.startPoint[1] - endPoint[1]
33813                 );
33814         }
33815         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33816         if(newSize != this.dragSpecs.startSize){
33817             if(this.fireEvent('beforeapply', this, newSize) !== false){
33818                 this.adapter.setElementSize(this, newSize);
33819                 this.fireEvent("moved", this, newSize);
33820                 this.fireEvent("resize", this, newSize);
33821             }
33822         }
33823     },
33824     
33825     /**
33826      * Get the adapter this SplitBar uses
33827      * @return The adapter object
33828      */
33829     getAdapter : function(){
33830         return this.adapter;
33831     },
33832     
33833     /**
33834      * Set the adapter this SplitBar uses
33835      * @param {Object} adapter A SplitBar adapter object
33836      */
33837     setAdapter : function(adapter){
33838         this.adapter = adapter;
33839         this.adapter.init(this);
33840     },
33841     
33842     /**
33843      * Gets the minimum size for the resizing element
33844      * @return {Number} The minimum size
33845      */
33846     getMinimumSize : function(){
33847         return this.minSize;
33848     },
33849     
33850     /**
33851      * Sets the minimum size for the resizing element
33852      * @param {Number} minSize The minimum size
33853      */
33854     setMinimumSize : function(minSize){
33855         this.minSize = minSize;
33856     },
33857     
33858     /**
33859      * Gets the maximum size for the resizing element
33860      * @return {Number} The maximum size
33861      */
33862     getMaximumSize : function(){
33863         return this.maxSize;
33864     },
33865     
33866     /**
33867      * Sets the maximum size for the resizing element
33868      * @param {Number} maxSize The maximum size
33869      */
33870     setMaximumSize : function(maxSize){
33871         this.maxSize = maxSize;
33872     },
33873     
33874     /**
33875      * Sets the initialize size for the resizing element
33876      * @param {Number} size The initial size
33877      */
33878     setCurrentSize : function(size){
33879         var oldAnimate = this.animate;
33880         this.animate = false;
33881         this.adapter.setElementSize(this, size);
33882         this.animate = oldAnimate;
33883     },
33884     
33885     /**
33886      * Destroy this splitbar. 
33887      * @param {Boolean} removeEl True to remove the element
33888      */
33889     destroy : function(removeEl){
33890         if(this.shim){
33891             this.shim.remove();
33892         }
33893         this.dd.unreg();
33894         this.proxy.parentNode.removeChild(this.proxy);
33895         if(removeEl){
33896             this.el.remove();
33897         }
33898     }
33899 });
33900
33901 /**
33902  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
33903  */
33904 Roo.bootstrap.SplitBar.createProxy = function(dir){
33905     var proxy = new Roo.Element(document.createElement("div"));
33906     proxy.unselectable();
33907     var cls = 'roo-splitbar-proxy';
33908     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33909     document.body.appendChild(proxy.dom);
33910     return proxy.dom;
33911 };
33912
33913 /** 
33914  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33915  * Default Adapter. It assumes the splitter and resizing element are not positioned
33916  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33917  */
33918 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33919 };
33920
33921 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33922     // do nothing for now
33923     init : function(s){
33924     
33925     },
33926     /**
33927      * Called before drag operations to get the current size of the resizing element. 
33928      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33929      */
33930      getElementSize : function(s){
33931         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33932             return s.resizingEl.getWidth();
33933         }else{
33934             return s.resizingEl.getHeight();
33935         }
33936     },
33937     
33938     /**
33939      * Called after drag operations to set the size of the resizing element.
33940      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33941      * @param {Number} newSize The new size to set
33942      * @param {Function} onComplete A function to be invoked when resizing is complete
33943      */
33944     setElementSize : function(s, newSize, onComplete){
33945         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33946             if(!s.animate){
33947                 s.resizingEl.setWidth(newSize);
33948                 if(onComplete){
33949                     onComplete(s, newSize);
33950                 }
33951             }else{
33952                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33953             }
33954         }else{
33955             
33956             if(!s.animate){
33957                 s.resizingEl.setHeight(newSize);
33958                 if(onComplete){
33959                     onComplete(s, newSize);
33960                 }
33961             }else{
33962                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33963             }
33964         }
33965     }
33966 };
33967
33968 /** 
33969  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33970  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33971  * Adapter that  moves the splitter element to align with the resized sizing element. 
33972  * Used with an absolute positioned SplitBar.
33973  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33974  * document.body, make sure you assign an id to the body element.
33975  */
33976 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33977     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33978     this.container = Roo.get(container);
33979 };
33980
33981 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33982     init : function(s){
33983         this.basic.init(s);
33984     },
33985     
33986     getElementSize : function(s){
33987         return this.basic.getElementSize(s);
33988     },
33989     
33990     setElementSize : function(s, newSize, onComplete){
33991         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33992     },
33993     
33994     moveSplitter : function(s){
33995         var yes = Roo.bootstrap.SplitBar;
33996         switch(s.placement){
33997             case yes.LEFT:
33998                 s.el.setX(s.resizingEl.getRight());
33999                 break;
34000             case yes.RIGHT:
34001                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34002                 break;
34003             case yes.TOP:
34004                 s.el.setY(s.resizingEl.getBottom());
34005                 break;
34006             case yes.BOTTOM:
34007                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34008                 break;
34009         }
34010     }
34011 };
34012
34013 /**
34014  * Orientation constant - Create a vertical SplitBar
34015  * @static
34016  * @type Number
34017  */
34018 Roo.bootstrap.SplitBar.VERTICAL = 1;
34019
34020 /**
34021  * Orientation constant - Create a horizontal SplitBar
34022  * @static
34023  * @type Number
34024  */
34025 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34026
34027 /**
34028  * Placement constant - The resizing element is to the left of the splitter element
34029  * @static
34030  * @type Number
34031  */
34032 Roo.bootstrap.SplitBar.LEFT = 1;
34033
34034 /**
34035  * Placement constant - The resizing element is to the right of the splitter element
34036  * @static
34037  * @type Number
34038  */
34039 Roo.bootstrap.SplitBar.RIGHT = 2;
34040
34041 /**
34042  * Placement constant - The resizing element is positioned above the splitter element
34043  * @static
34044  * @type Number
34045  */
34046 Roo.bootstrap.SplitBar.TOP = 3;
34047
34048 /**
34049  * Placement constant - The resizing element is positioned under splitter element
34050  * @static
34051  * @type Number
34052  */
34053 Roo.bootstrap.SplitBar.BOTTOM = 4;
34054 Roo.namespace("Roo.bootstrap.layout");/*
34055  * Based on:
34056  * Ext JS Library 1.1.1
34057  * Copyright(c) 2006-2007, Ext JS, LLC.
34058  *
34059  * Originally Released Under LGPL - original licence link has changed is not relivant.
34060  *
34061  * Fork - LGPL
34062  * <script type="text/javascript">
34063  */
34064
34065 /**
34066  * @class Roo.bootstrap.layout.Manager
34067  * @extends Roo.bootstrap.Component
34068  * Base class for layout managers.
34069  */
34070 Roo.bootstrap.layout.Manager = function(config)
34071 {
34072     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34073
34074
34075
34076
34077
34078     /** false to disable window resize monitoring @type Boolean */
34079     this.monitorWindowResize = true;
34080     this.regions = {};
34081     this.addEvents({
34082         /**
34083          * @event layout
34084          * Fires when a layout is performed.
34085          * @param {Roo.LayoutManager} this
34086          */
34087         "layout" : true,
34088         /**
34089          * @event regionresized
34090          * Fires when the user resizes a region.
34091          * @param {Roo.LayoutRegion} region The resized region
34092          * @param {Number} newSize The new size (width for east/west, height for north/south)
34093          */
34094         "regionresized" : true,
34095         /**
34096          * @event regioncollapsed
34097          * Fires when a region is collapsed.
34098          * @param {Roo.LayoutRegion} region The collapsed region
34099          */
34100         "regioncollapsed" : true,
34101         /**
34102          * @event regionexpanded
34103          * Fires when a region is expanded.
34104          * @param {Roo.LayoutRegion} region The expanded region
34105          */
34106         "regionexpanded" : true
34107     });
34108     this.updating = false;
34109
34110     if (config.el) {
34111         this.el = Roo.get(config.el);
34112         this.initEvents();
34113     }
34114
34115 };
34116
34117 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34118
34119
34120     regions : null,
34121
34122     monitorWindowResize : true,
34123
34124
34125     updating : false,
34126
34127
34128     onRender : function(ct, position)
34129     {
34130         if(!this.el){
34131             this.el = Roo.get(ct);
34132             this.initEvents();
34133         }
34134         //this.fireEvent('render',this);
34135     },
34136
34137
34138     initEvents: function()
34139     {
34140
34141
34142         // ie scrollbar fix
34143         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34144             document.body.scroll = "no";
34145         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34146             this.el.position('relative');
34147         }
34148         this.id = this.el.id;
34149         this.el.addClass("roo-layout-container");
34150         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34151         if(this.el.dom != document.body ) {
34152             this.el.on('resize', this.layout,this);
34153             this.el.on('show', this.layout,this);
34154         }
34155
34156     },
34157
34158     /**
34159      * Returns true if this layout is currently being updated
34160      * @return {Boolean}
34161      */
34162     isUpdating : function(){
34163         return this.updating;
34164     },
34165
34166     /**
34167      * Suspend the LayoutManager from doing auto-layouts while
34168      * making multiple add or remove calls
34169      */
34170     beginUpdate : function(){
34171         this.updating = true;
34172     },
34173
34174     /**
34175      * Restore auto-layouts and optionally disable the manager from performing a layout
34176      * @param {Boolean} noLayout true to disable a layout update
34177      */
34178     endUpdate : function(noLayout){
34179         this.updating = false;
34180         if(!noLayout){
34181             this.layout();
34182         }
34183     },
34184
34185     layout: function(){
34186         // abstract...
34187     },
34188
34189     onRegionResized : function(region, newSize){
34190         this.fireEvent("regionresized", region, newSize);
34191         this.layout();
34192     },
34193
34194     onRegionCollapsed : function(region){
34195         this.fireEvent("regioncollapsed", region);
34196     },
34197
34198     onRegionExpanded : function(region){
34199         this.fireEvent("regionexpanded", region);
34200     },
34201
34202     /**
34203      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34204      * performs box-model adjustments.
34205      * @return {Object} The size as an object {width: (the width), height: (the height)}
34206      */
34207     getViewSize : function()
34208     {
34209         var size;
34210         if(this.el.dom != document.body){
34211             size = this.el.getSize();
34212         }else{
34213             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34214         }
34215         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34216         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34217         return size;
34218     },
34219
34220     /**
34221      * Returns the Element this layout is bound to.
34222      * @return {Roo.Element}
34223      */
34224     getEl : function(){
34225         return this.el;
34226     },
34227
34228     /**
34229      * Returns the specified region.
34230      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34231      * @return {Roo.LayoutRegion}
34232      */
34233     getRegion : function(target){
34234         return this.regions[target.toLowerCase()];
34235     },
34236
34237     onWindowResize : function(){
34238         if(this.monitorWindowResize){
34239             this.layout();
34240         }
34241     }
34242 });
34243 /*
34244  * Based on:
34245  * Ext JS Library 1.1.1
34246  * Copyright(c) 2006-2007, Ext JS, LLC.
34247  *
34248  * Originally Released Under LGPL - original licence link has changed is not relivant.
34249  *
34250  * Fork - LGPL
34251  * <script type="text/javascript">
34252  */
34253 /**
34254  * @class Roo.bootstrap.layout.Border
34255  * @extends Roo.bootstrap.layout.Manager
34256  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34257  * please see: examples/bootstrap/nested.html<br><br>
34258  
34259 <b>The container the layout is rendered into can be either the body element or any other element.
34260 If it is not the body element, the container needs to either be an absolute positioned element,
34261 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34262 the container size if it is not the body element.</b>
34263
34264 * @constructor
34265 * Create a new Border
34266 * @param {Object} config Configuration options
34267  */
34268 Roo.bootstrap.layout.Border = function(config){
34269     config = config || {};
34270     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34271     
34272     
34273     
34274     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34275         if(config[region]){
34276             config[region].region = region;
34277             this.addRegion(config[region]);
34278         }
34279     },this);
34280     
34281 };
34282
34283 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34284
34285 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34286     /**
34287      * Creates and adds a new region if it doesn't already exist.
34288      * @param {String} target The target region key (north, south, east, west or center).
34289      * @param {Object} config The regions config object
34290      * @return {BorderLayoutRegion} The new region
34291      */
34292     addRegion : function(config)
34293     {
34294         if(!this.regions[config.region]){
34295             var r = this.factory(config);
34296             this.bindRegion(r);
34297         }
34298         return this.regions[config.region];
34299     },
34300
34301     // private (kinda)
34302     bindRegion : function(r){
34303         this.regions[r.config.region] = r;
34304         
34305         r.on("visibilitychange",    this.layout, this);
34306         r.on("paneladded",          this.layout, this);
34307         r.on("panelremoved",        this.layout, this);
34308         r.on("invalidated",         this.layout, this);
34309         r.on("resized",             this.onRegionResized, this);
34310         r.on("collapsed",           this.onRegionCollapsed, this);
34311         r.on("expanded",            this.onRegionExpanded, this);
34312     },
34313
34314     /**
34315      * Performs a layout update.
34316      */
34317     layout : function()
34318     {
34319         if(this.updating) {
34320             return;
34321         }
34322         
34323         // render all the rebions if they have not been done alreayd?
34324         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34325             if(this.regions[region] && !this.regions[region].bodyEl){
34326                 this.regions[region].onRender(this.el)
34327             }
34328         },this);
34329         
34330         var size = this.getViewSize();
34331         var w = size.width;
34332         var h = size.height;
34333         var centerW = w;
34334         var centerH = h;
34335         var centerY = 0;
34336         var centerX = 0;
34337         //var x = 0, y = 0;
34338
34339         var rs = this.regions;
34340         var north = rs["north"];
34341         var south = rs["south"]; 
34342         var west = rs["west"];
34343         var east = rs["east"];
34344         var center = rs["center"];
34345         //if(this.hideOnLayout){ // not supported anymore
34346             //c.el.setStyle("display", "none");
34347         //}
34348         if(north && north.isVisible()){
34349             var b = north.getBox();
34350             var m = north.getMargins();
34351             b.width = w - (m.left+m.right);
34352             b.x = m.left;
34353             b.y = m.top;
34354             centerY = b.height + b.y + m.bottom;
34355             centerH -= centerY;
34356             north.updateBox(this.safeBox(b));
34357         }
34358         if(south && south.isVisible()){
34359             var b = south.getBox();
34360             var m = south.getMargins();
34361             b.width = w - (m.left+m.right);
34362             b.x = m.left;
34363             var totalHeight = (b.height + m.top + m.bottom);
34364             b.y = h - totalHeight + m.top;
34365             centerH -= totalHeight;
34366             south.updateBox(this.safeBox(b));
34367         }
34368         if(west && west.isVisible()){
34369             var b = west.getBox();
34370             var m = west.getMargins();
34371             b.height = centerH - (m.top+m.bottom);
34372             b.x = m.left;
34373             b.y = centerY + m.top;
34374             var totalWidth = (b.width + m.left + m.right);
34375             centerX += totalWidth;
34376             centerW -= totalWidth;
34377             west.updateBox(this.safeBox(b));
34378         }
34379         if(east && east.isVisible()){
34380             var b = east.getBox();
34381             var m = east.getMargins();
34382             b.height = centerH - (m.top+m.bottom);
34383             var totalWidth = (b.width + m.left + m.right);
34384             b.x = w - totalWidth + m.left;
34385             b.y = centerY + m.top;
34386             centerW -= totalWidth;
34387             east.updateBox(this.safeBox(b));
34388         }
34389         if(center){
34390             var m = center.getMargins();
34391             var centerBox = {
34392                 x: centerX + m.left,
34393                 y: centerY + m.top,
34394                 width: centerW - (m.left+m.right),
34395                 height: centerH - (m.top+m.bottom)
34396             };
34397             //if(this.hideOnLayout){
34398                 //center.el.setStyle("display", "block");
34399             //}
34400             center.updateBox(this.safeBox(centerBox));
34401         }
34402         this.el.repaint();
34403         this.fireEvent("layout", this);
34404     },
34405
34406     // private
34407     safeBox : function(box){
34408         box.width = Math.max(0, box.width);
34409         box.height = Math.max(0, box.height);
34410         return box;
34411     },
34412
34413     /**
34414      * Adds a ContentPanel (or subclass) to this layout.
34415      * @param {String} target The target region key (north, south, east, west or center).
34416      * @param {Roo.ContentPanel} panel The panel to add
34417      * @return {Roo.ContentPanel} The added panel
34418      */
34419     add : function(target, panel){
34420          
34421         target = target.toLowerCase();
34422         return this.regions[target].add(panel);
34423     },
34424
34425     /**
34426      * Remove a ContentPanel (or subclass) to this layout.
34427      * @param {String} target The target region key (north, south, east, west or center).
34428      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34429      * @return {Roo.ContentPanel} The removed panel
34430      */
34431     remove : function(target, panel){
34432         target = target.toLowerCase();
34433         return this.regions[target].remove(panel);
34434     },
34435
34436     /**
34437      * Searches all regions for a panel with the specified id
34438      * @param {String} panelId
34439      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34440      */
34441     findPanel : function(panelId){
34442         var rs = this.regions;
34443         for(var target in rs){
34444             if(typeof rs[target] != "function"){
34445                 var p = rs[target].getPanel(panelId);
34446                 if(p){
34447                     return p;
34448                 }
34449             }
34450         }
34451         return null;
34452     },
34453
34454     /**
34455      * Searches all regions for a panel with the specified id and activates (shows) it.
34456      * @param {String/ContentPanel} panelId The panels id or the panel itself
34457      * @return {Roo.ContentPanel} The shown panel or null
34458      */
34459     showPanel : function(panelId) {
34460       var rs = this.regions;
34461       for(var target in rs){
34462          var r = rs[target];
34463          if(typeof r != "function"){
34464             if(r.hasPanel(panelId)){
34465                return r.showPanel(panelId);
34466             }
34467          }
34468       }
34469       return null;
34470    },
34471
34472    /**
34473      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34474      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34475      */
34476    /*
34477     restoreState : function(provider){
34478         if(!provider){
34479             provider = Roo.state.Manager;
34480         }
34481         var sm = new Roo.LayoutStateManager();
34482         sm.init(this, provider);
34483     },
34484 */
34485  
34486  
34487     /**
34488      * Adds a xtype elements to the layout.
34489      * <pre><code>
34490
34491 layout.addxtype({
34492        xtype : 'ContentPanel',
34493        region: 'west',
34494        items: [ .... ]
34495    }
34496 );
34497
34498 layout.addxtype({
34499         xtype : 'NestedLayoutPanel',
34500         region: 'west',
34501         layout: {
34502            center: { },
34503            west: { }   
34504         },
34505         items : [ ... list of content panels or nested layout panels.. ]
34506    }
34507 );
34508 </code></pre>
34509      * @param {Object} cfg Xtype definition of item to add.
34510      */
34511     addxtype : function(cfg)
34512     {
34513         // basically accepts a pannel...
34514         // can accept a layout region..!?!?
34515         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34516         
34517         
34518         // theory?  children can only be panels??
34519         
34520         //if (!cfg.xtype.match(/Panel$/)) {
34521         //    return false;
34522         //}
34523         var ret = false;
34524         
34525         if (typeof(cfg.region) == 'undefined') {
34526             Roo.log("Failed to add Panel, region was not set");
34527             Roo.log(cfg);
34528             return false;
34529         }
34530         var region = cfg.region;
34531         delete cfg.region;
34532         
34533           
34534         var xitems = [];
34535         if (cfg.items) {
34536             xitems = cfg.items;
34537             delete cfg.items;
34538         }
34539         var nb = false;
34540         
34541         switch(cfg.xtype) 
34542         {
34543             case 'Content':  // ContentPanel (el, cfg)
34544             case 'Scroll':  // ContentPanel (el, cfg)
34545             case 'View': 
34546                 cfg.autoCreate = true;
34547                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34548                 //} else {
34549                 //    var el = this.el.createChild();
34550                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34551                 //}
34552                 
34553                 this.add(region, ret);
34554                 break;
34555             
34556             /*
34557             case 'TreePanel': // our new panel!
34558                 cfg.el = this.el.createChild();
34559                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34560                 this.add(region, ret);
34561                 break;
34562             */
34563             
34564             case 'Nest': 
34565                 // create a new Layout (which is  a Border Layout...
34566                 
34567                 var clayout = cfg.layout;
34568                 clayout.el  = this.el.createChild();
34569                 clayout.items   = clayout.items  || [];
34570                 
34571                 delete cfg.layout;
34572                 
34573                 // replace this exitems with the clayout ones..
34574                 xitems = clayout.items;
34575                  
34576                 // force background off if it's in center...
34577                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34578                     cfg.background = false;
34579                 }
34580                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34581                 
34582                 
34583                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34584                 //console.log('adding nested layout panel '  + cfg.toSource());
34585                 this.add(region, ret);
34586                 nb = {}; /// find first...
34587                 break;
34588             
34589             case 'Grid':
34590                 
34591                 // needs grid and region
34592                 
34593                 //var el = this.getRegion(region).el.createChild();
34594                 /*
34595                  *var el = this.el.createChild();
34596                 // create the grid first...
34597                 cfg.grid.container = el;
34598                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34599                 */
34600                 
34601                 if (region == 'center' && this.active ) {
34602                     cfg.background = false;
34603                 }
34604                 
34605                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34606                 
34607                 this.add(region, ret);
34608                 /*
34609                 if (cfg.background) {
34610                     // render grid on panel activation (if panel background)
34611                     ret.on('activate', function(gp) {
34612                         if (!gp.grid.rendered) {
34613                     //        gp.grid.render(el);
34614                         }
34615                     });
34616                 } else {
34617                   //  cfg.grid.render(el);
34618                 }
34619                 */
34620                 break;
34621            
34622            
34623             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34624                 // it was the old xcomponent building that caused this before.
34625                 // espeically if border is the top element in the tree.
34626                 ret = this;
34627                 break; 
34628                 
34629                     
34630                 
34631                 
34632                 
34633             default:
34634                 /*
34635                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34636                     
34637                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34638                     this.add(region, ret);
34639                 } else {
34640                 */
34641                     Roo.log(cfg);
34642                     throw "Can not add '" + cfg.xtype + "' to Border";
34643                     return null;
34644              
34645                                 
34646              
34647         }
34648         this.beginUpdate();
34649         // add children..
34650         var region = '';
34651         var abn = {};
34652         Roo.each(xitems, function(i)  {
34653             region = nb && i.region ? i.region : false;
34654             
34655             var add = ret.addxtype(i);
34656            
34657             if (region) {
34658                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34659                 if (!i.background) {
34660                     abn[region] = nb[region] ;
34661                 }
34662             }
34663             
34664         });
34665         this.endUpdate();
34666
34667         // make the last non-background panel active..
34668         //if (nb) { Roo.log(abn); }
34669         if (nb) {
34670             
34671             for(var r in abn) {
34672                 region = this.getRegion(r);
34673                 if (region) {
34674                     // tried using nb[r], but it does not work..
34675                      
34676                     region.showPanel(abn[r]);
34677                    
34678                 }
34679             }
34680         }
34681         return ret;
34682         
34683     },
34684     
34685     
34686 // private
34687     factory : function(cfg)
34688     {
34689         
34690         var validRegions = Roo.bootstrap.layout.Border.regions;
34691
34692         var target = cfg.region;
34693         cfg.mgr = this;
34694         
34695         var r = Roo.bootstrap.layout;
34696         Roo.log(target);
34697         switch(target){
34698             case "north":
34699                 return new r.North(cfg);
34700             case "south":
34701                 return new r.South(cfg);
34702             case "east":
34703                 return new r.East(cfg);
34704             case "west":
34705                 return new r.West(cfg);
34706             case "center":
34707                 return new r.Center(cfg);
34708         }
34709         throw 'Layout region "'+target+'" not supported.';
34710     }
34711     
34712     
34713 });
34714  /*
34715  * Based on:
34716  * Ext JS Library 1.1.1
34717  * Copyright(c) 2006-2007, Ext JS, LLC.
34718  *
34719  * Originally Released Under LGPL - original licence link has changed is not relivant.
34720  *
34721  * Fork - LGPL
34722  * <script type="text/javascript">
34723  */
34724  
34725 /**
34726  * @class Roo.bootstrap.layout.Basic
34727  * @extends Roo.util.Observable
34728  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34729  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34730  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34731  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34732  * @cfg {string}   region  the region that it inhabits..
34733  * @cfg {bool}   skipConfig skip config?
34734  * 
34735
34736  */
34737 Roo.bootstrap.layout.Basic = function(config){
34738     
34739     this.mgr = config.mgr;
34740     
34741     this.position = config.region;
34742     
34743     var skipConfig = config.skipConfig;
34744     
34745     this.events = {
34746         /**
34747          * @scope Roo.BasicLayoutRegion
34748          */
34749         
34750         /**
34751          * @event beforeremove
34752          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34753          * @param {Roo.LayoutRegion} this
34754          * @param {Roo.ContentPanel} panel The panel
34755          * @param {Object} e The cancel event object
34756          */
34757         "beforeremove" : true,
34758         /**
34759          * @event invalidated
34760          * Fires when the layout for this region is changed.
34761          * @param {Roo.LayoutRegion} this
34762          */
34763         "invalidated" : true,
34764         /**
34765          * @event visibilitychange
34766          * Fires when this region is shown or hidden 
34767          * @param {Roo.LayoutRegion} this
34768          * @param {Boolean} visibility true or false
34769          */
34770         "visibilitychange" : true,
34771         /**
34772          * @event paneladded
34773          * Fires when a panel is added. 
34774          * @param {Roo.LayoutRegion} this
34775          * @param {Roo.ContentPanel} panel The panel
34776          */
34777         "paneladded" : true,
34778         /**
34779          * @event panelremoved
34780          * Fires when a panel is removed. 
34781          * @param {Roo.LayoutRegion} this
34782          * @param {Roo.ContentPanel} panel The panel
34783          */
34784         "panelremoved" : true,
34785         /**
34786          * @event beforecollapse
34787          * Fires when this region before collapse.
34788          * @param {Roo.LayoutRegion} this
34789          */
34790         "beforecollapse" : true,
34791         /**
34792          * @event collapsed
34793          * Fires when this region is collapsed.
34794          * @param {Roo.LayoutRegion} this
34795          */
34796         "collapsed" : true,
34797         /**
34798          * @event expanded
34799          * Fires when this region is expanded.
34800          * @param {Roo.LayoutRegion} this
34801          */
34802         "expanded" : true,
34803         /**
34804          * @event slideshow
34805          * Fires when this region is slid into view.
34806          * @param {Roo.LayoutRegion} this
34807          */
34808         "slideshow" : true,
34809         /**
34810          * @event slidehide
34811          * Fires when this region slides out of view. 
34812          * @param {Roo.LayoutRegion} this
34813          */
34814         "slidehide" : true,
34815         /**
34816          * @event panelactivated
34817          * Fires when a panel is activated. 
34818          * @param {Roo.LayoutRegion} this
34819          * @param {Roo.ContentPanel} panel The activated panel
34820          */
34821         "panelactivated" : true,
34822         /**
34823          * @event resized
34824          * Fires when the user resizes this region. 
34825          * @param {Roo.LayoutRegion} this
34826          * @param {Number} newSize The new size (width for east/west, height for north/south)
34827          */
34828         "resized" : true
34829     };
34830     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34831     this.panels = new Roo.util.MixedCollection();
34832     this.panels.getKey = this.getPanelId.createDelegate(this);
34833     this.box = null;
34834     this.activePanel = null;
34835     // ensure listeners are added...
34836     
34837     if (config.listeners || config.events) {
34838         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34839             listeners : config.listeners || {},
34840             events : config.events || {}
34841         });
34842     }
34843     
34844     if(skipConfig !== true){
34845         this.applyConfig(config);
34846     }
34847 };
34848
34849 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34850 {
34851     getPanelId : function(p){
34852         return p.getId();
34853     },
34854     
34855     applyConfig : function(config){
34856         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34857         this.config = config;
34858         
34859     },
34860     
34861     /**
34862      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34863      * the width, for horizontal (north, south) the height.
34864      * @param {Number} newSize The new width or height
34865      */
34866     resizeTo : function(newSize){
34867         var el = this.el ? this.el :
34868                  (this.activePanel ? this.activePanel.getEl() : null);
34869         if(el){
34870             switch(this.position){
34871                 case "east":
34872                 case "west":
34873                     el.setWidth(newSize);
34874                     this.fireEvent("resized", this, newSize);
34875                 break;
34876                 case "north":
34877                 case "south":
34878                     el.setHeight(newSize);
34879                     this.fireEvent("resized", this, newSize);
34880                 break;                
34881             }
34882         }
34883     },
34884     
34885     getBox : function(){
34886         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34887     },
34888     
34889     getMargins : function(){
34890         return this.margins;
34891     },
34892     
34893     updateBox : function(box){
34894         this.box = box;
34895         var el = this.activePanel.getEl();
34896         el.dom.style.left = box.x + "px";
34897         el.dom.style.top = box.y + "px";
34898         this.activePanel.setSize(box.width, box.height);
34899     },
34900     
34901     /**
34902      * Returns the container element for this region.
34903      * @return {Roo.Element}
34904      */
34905     getEl : function(){
34906         return this.activePanel;
34907     },
34908     
34909     /**
34910      * Returns true if this region is currently visible.
34911      * @return {Boolean}
34912      */
34913     isVisible : function(){
34914         return this.activePanel ? true : false;
34915     },
34916     
34917     setActivePanel : function(panel){
34918         panel = this.getPanel(panel);
34919         if(this.activePanel && this.activePanel != panel){
34920             this.activePanel.setActiveState(false);
34921             this.activePanel.getEl().setLeftTop(-10000,-10000);
34922         }
34923         this.activePanel = panel;
34924         panel.setActiveState(true);
34925         if(this.box){
34926             panel.setSize(this.box.width, this.box.height);
34927         }
34928         this.fireEvent("panelactivated", this, panel);
34929         this.fireEvent("invalidated");
34930     },
34931     
34932     /**
34933      * Show the specified panel.
34934      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34935      * @return {Roo.ContentPanel} The shown panel or null
34936      */
34937     showPanel : function(panel){
34938         panel = this.getPanel(panel);
34939         if(panel){
34940             this.setActivePanel(panel);
34941         }
34942         return panel;
34943     },
34944     
34945     /**
34946      * Get the active panel for this region.
34947      * @return {Roo.ContentPanel} The active panel or null
34948      */
34949     getActivePanel : function(){
34950         return this.activePanel;
34951     },
34952     
34953     /**
34954      * Add the passed ContentPanel(s)
34955      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34956      * @return {Roo.ContentPanel} The panel added (if only one was added)
34957      */
34958     add : function(panel){
34959         if(arguments.length > 1){
34960             for(var i = 0, len = arguments.length; i < len; i++) {
34961                 this.add(arguments[i]);
34962             }
34963             return null;
34964         }
34965         if(this.hasPanel(panel)){
34966             this.showPanel(panel);
34967             return panel;
34968         }
34969         var el = panel.getEl();
34970         if(el.dom.parentNode != this.mgr.el.dom){
34971             this.mgr.el.dom.appendChild(el.dom);
34972         }
34973         if(panel.setRegion){
34974             panel.setRegion(this);
34975         }
34976         this.panels.add(panel);
34977         el.setStyle("position", "absolute");
34978         if(!panel.background){
34979             this.setActivePanel(panel);
34980             if(this.config.initialSize && this.panels.getCount()==1){
34981                 this.resizeTo(this.config.initialSize);
34982             }
34983         }
34984         this.fireEvent("paneladded", this, panel);
34985         return panel;
34986     },
34987     
34988     /**
34989      * Returns true if the panel is in this region.
34990      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34991      * @return {Boolean}
34992      */
34993     hasPanel : function(panel){
34994         if(typeof panel == "object"){ // must be panel obj
34995             panel = panel.getId();
34996         }
34997         return this.getPanel(panel) ? true : false;
34998     },
34999     
35000     /**
35001      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35002      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35003      * @param {Boolean} preservePanel Overrides the config preservePanel option
35004      * @return {Roo.ContentPanel} The panel that was removed
35005      */
35006     remove : function(panel, preservePanel){
35007         panel = this.getPanel(panel);
35008         if(!panel){
35009             return null;
35010         }
35011         var e = {};
35012         this.fireEvent("beforeremove", this, panel, e);
35013         if(e.cancel === true){
35014             return null;
35015         }
35016         var panelId = panel.getId();
35017         this.panels.removeKey(panelId);
35018         return panel;
35019     },
35020     
35021     /**
35022      * Returns the panel specified or null if it's not in this region.
35023      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35024      * @return {Roo.ContentPanel}
35025      */
35026     getPanel : function(id){
35027         if(typeof id == "object"){ // must be panel obj
35028             return id;
35029         }
35030         return this.panels.get(id);
35031     },
35032     
35033     /**
35034      * Returns this regions position (north/south/east/west/center).
35035      * @return {String} 
35036      */
35037     getPosition: function(){
35038         return this.position;    
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050  
35051 /**
35052  * @class Roo.bootstrap.layout.Region
35053  * @extends Roo.bootstrap.layout.Basic
35054  * This class represents a region in a layout manager.
35055  
35056  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35057  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35058  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35059  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35060  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35061  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35062  * @cfg {String}    title           The title for the region (overrides panel titles)
35063  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35064  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35065  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35066  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35067  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35068  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35069  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35070  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35071  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35072  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35073
35074  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35075  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35076  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35077  * @cfg {Number}    width           For East/West panels
35078  * @cfg {Number}    height          For North/South panels
35079  * @cfg {Boolean}   split           To show the splitter
35080  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35081  * 
35082  * @cfg {string}   cls             Extra CSS classes to add to region
35083  * 
35084  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35085  * @cfg {string}   region  the region that it inhabits..
35086  *
35087
35088  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35089  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35090
35091  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35092  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35093  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35094  */
35095 Roo.bootstrap.layout.Region = function(config)
35096 {
35097     this.applyConfig(config);
35098
35099     var mgr = config.mgr;
35100     var pos = config.region;
35101     config.skipConfig = true;
35102     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35103     
35104     if (mgr.el) {
35105         this.onRender(mgr.el);   
35106     }
35107      
35108     this.visible = true;
35109     this.collapsed = false;
35110     this.unrendered_panels = [];
35111 };
35112
35113 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35114
35115     position: '', // set by wrapper (eg. north/south etc..)
35116     unrendered_panels : null,  // unrendered panels.
35117     createBody : function(){
35118         /** This region's body element 
35119         * @type Roo.Element */
35120         this.bodyEl = this.el.createChild({
35121                 tag: "div",
35122                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35123         });
35124     },
35125
35126     onRender: function(ctr, pos)
35127     {
35128         var dh = Roo.DomHelper;
35129         /** This region's container element 
35130         * @type Roo.Element */
35131         this.el = dh.append(ctr.dom, {
35132                 tag: "div",
35133                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35134             }, true);
35135         /** This region's title element 
35136         * @type Roo.Element */
35137     
35138         this.titleEl = dh.append(this.el.dom,
35139             {
35140                     tag: "div",
35141                     unselectable: "on",
35142                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35143                     children:[
35144                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35145                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35146                     ]}, true);
35147         
35148         this.titleEl.enableDisplayMode();
35149         /** This region's title text element 
35150         * @type HTMLElement */
35151         this.titleTextEl = this.titleEl.dom.firstChild;
35152         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35153         /*
35154         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35155         this.closeBtn.enableDisplayMode();
35156         this.closeBtn.on("click", this.closeClicked, this);
35157         this.closeBtn.hide();
35158     */
35159         this.createBody(this.config);
35160         if(this.config.hideWhenEmpty){
35161             this.hide();
35162             this.on("paneladded", this.validateVisibility, this);
35163             this.on("panelremoved", this.validateVisibility, this);
35164         }
35165         if(this.autoScroll){
35166             this.bodyEl.setStyle("overflow", "auto");
35167         }else{
35168             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35169         }
35170         //if(c.titlebar !== false){
35171             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35172                 this.titleEl.hide();
35173             }else{
35174                 this.titleEl.show();
35175                 if(this.config.title){
35176                     this.titleTextEl.innerHTML = this.config.title;
35177                 }
35178             }
35179         //}
35180         if(this.config.collapsed){
35181             this.collapse(true);
35182         }
35183         if(this.config.hidden){
35184             this.hide();
35185         }
35186         
35187         if (this.unrendered_panels && this.unrendered_panels.length) {
35188             for (var i =0;i< this.unrendered_panels.length; i++) {
35189                 this.add(this.unrendered_panels[i]);
35190             }
35191             this.unrendered_panels = null;
35192             
35193         }
35194         
35195     },
35196     
35197     applyConfig : function(c)
35198     {
35199         /*
35200          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35201             var dh = Roo.DomHelper;
35202             if(c.titlebar !== false){
35203                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35204                 this.collapseBtn.on("click", this.collapse, this);
35205                 this.collapseBtn.enableDisplayMode();
35206                 /*
35207                 if(c.showPin === true || this.showPin){
35208                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35209                     this.stickBtn.enableDisplayMode();
35210                     this.stickBtn.on("click", this.expand, this);
35211                     this.stickBtn.hide();
35212                 }
35213                 
35214             }
35215             */
35216             /** This region's collapsed element
35217             * @type Roo.Element */
35218             /*
35219              *
35220             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35221                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35222             ]}, true);
35223             
35224             if(c.floatable !== false){
35225                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35226                this.collapsedEl.on("click", this.collapseClick, this);
35227             }
35228
35229             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35230                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35231                    id: "message", unselectable: "on", style:{"float":"left"}});
35232                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35233              }
35234             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35235             this.expandBtn.on("click", this.expand, this);
35236             
35237         }
35238         
35239         if(this.collapseBtn){
35240             this.collapseBtn.setVisible(c.collapsible == true);
35241         }
35242         
35243         this.cmargins = c.cmargins || this.cmargins ||
35244                          (this.position == "west" || this.position == "east" ?
35245                              {top: 0, left: 2, right:2, bottom: 0} :
35246                              {top: 2, left: 0, right:0, bottom: 2});
35247         */
35248         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35249         
35250         
35251         this.bottomTabs = c.tabPosition != "top";
35252         
35253         this.autoScroll = c.autoScroll || false;
35254         
35255         
35256        
35257         
35258         this.duration = c.duration || .30;
35259         this.slideDuration = c.slideDuration || .45;
35260         this.config = c;
35261        
35262     },
35263     /**
35264      * Returns true if this region is currently visible.
35265      * @return {Boolean}
35266      */
35267     isVisible : function(){
35268         return this.visible;
35269     },
35270
35271     /**
35272      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35273      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35274      */
35275     //setCollapsedTitle : function(title){
35276     //    title = title || "&#160;";
35277      //   if(this.collapsedTitleTextEl){
35278       //      this.collapsedTitleTextEl.innerHTML = title;
35279        // }
35280     //},
35281
35282     getBox : function(){
35283         var b;
35284       //  if(!this.collapsed){
35285             b = this.el.getBox(false, true);
35286        // }else{
35287           //  b = this.collapsedEl.getBox(false, true);
35288         //}
35289         return b;
35290     },
35291
35292     getMargins : function(){
35293         return this.margins;
35294         //return this.collapsed ? this.cmargins : this.margins;
35295     },
35296 /*
35297     highlight : function(){
35298         this.el.addClass("x-layout-panel-dragover");
35299     },
35300
35301     unhighlight : function(){
35302         this.el.removeClass("x-layout-panel-dragover");
35303     },
35304 */
35305     updateBox : function(box)
35306     {
35307         if (!this.bodyEl) {
35308             return; // not rendered yet..
35309         }
35310         
35311         this.box = box;
35312         if(!this.collapsed){
35313             this.el.dom.style.left = box.x + "px";
35314             this.el.dom.style.top = box.y + "px";
35315             this.updateBody(box.width, box.height);
35316         }else{
35317             this.collapsedEl.dom.style.left = box.x + "px";
35318             this.collapsedEl.dom.style.top = box.y + "px";
35319             this.collapsedEl.setSize(box.width, box.height);
35320         }
35321         if(this.tabs){
35322             this.tabs.autoSizeTabs();
35323         }
35324     },
35325
35326     updateBody : function(w, h)
35327     {
35328         if(w !== null){
35329             this.el.setWidth(w);
35330             w -= this.el.getBorderWidth("rl");
35331             if(this.config.adjustments){
35332                 w += this.config.adjustments[0];
35333             }
35334         }
35335         if(h !== null && h > 0){
35336             this.el.setHeight(h);
35337             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35338             h -= this.el.getBorderWidth("tb");
35339             if(this.config.adjustments){
35340                 h += this.config.adjustments[1];
35341             }
35342             this.bodyEl.setHeight(h);
35343             if(this.tabs){
35344                 h = this.tabs.syncHeight(h);
35345             }
35346         }
35347         if(this.panelSize){
35348             w = w !== null ? w : this.panelSize.width;
35349             h = h !== null ? h : this.panelSize.height;
35350         }
35351         if(this.activePanel){
35352             var el = this.activePanel.getEl();
35353             w = w !== null ? w : el.getWidth();
35354             h = h !== null ? h : el.getHeight();
35355             this.panelSize = {width: w, height: h};
35356             this.activePanel.setSize(w, h);
35357         }
35358         if(Roo.isIE && this.tabs){
35359             this.tabs.el.repaint();
35360         }
35361     },
35362
35363     /**
35364      * Returns the container element for this region.
35365      * @return {Roo.Element}
35366      */
35367     getEl : function(){
35368         return this.el;
35369     },
35370
35371     /**
35372      * Hides this region.
35373      */
35374     hide : function(){
35375         //if(!this.collapsed){
35376             this.el.dom.style.left = "-2000px";
35377             this.el.hide();
35378         //}else{
35379          //   this.collapsedEl.dom.style.left = "-2000px";
35380          //   this.collapsedEl.hide();
35381        // }
35382         this.visible = false;
35383         this.fireEvent("visibilitychange", this, false);
35384     },
35385
35386     /**
35387      * Shows this region if it was previously hidden.
35388      */
35389     show : function(){
35390         //if(!this.collapsed){
35391             this.el.show();
35392         //}else{
35393         //    this.collapsedEl.show();
35394        // }
35395         this.visible = true;
35396         this.fireEvent("visibilitychange", this, true);
35397     },
35398 /*
35399     closeClicked : function(){
35400         if(this.activePanel){
35401             this.remove(this.activePanel);
35402         }
35403     },
35404
35405     collapseClick : function(e){
35406         if(this.isSlid){
35407            e.stopPropagation();
35408            this.slideIn();
35409         }else{
35410            e.stopPropagation();
35411            this.slideOut();
35412         }
35413     },
35414 */
35415     /**
35416      * Collapses this region.
35417      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35418      */
35419     /*
35420     collapse : function(skipAnim, skipCheck = false){
35421         if(this.collapsed) {
35422             return;
35423         }
35424         
35425         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35426             
35427             this.collapsed = true;
35428             if(this.split){
35429                 this.split.el.hide();
35430             }
35431             if(this.config.animate && skipAnim !== true){
35432                 this.fireEvent("invalidated", this);
35433                 this.animateCollapse();
35434             }else{
35435                 this.el.setLocation(-20000,-20000);
35436                 this.el.hide();
35437                 this.collapsedEl.show();
35438                 this.fireEvent("collapsed", this);
35439                 this.fireEvent("invalidated", this);
35440             }
35441         }
35442         
35443     },
35444 */
35445     animateCollapse : function(){
35446         // overridden
35447     },
35448
35449     /**
35450      * Expands this region if it was previously collapsed.
35451      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35452      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35453      */
35454     /*
35455     expand : function(e, skipAnim){
35456         if(e) {
35457             e.stopPropagation();
35458         }
35459         if(!this.collapsed || this.el.hasActiveFx()) {
35460             return;
35461         }
35462         if(this.isSlid){
35463             this.afterSlideIn();
35464             skipAnim = true;
35465         }
35466         this.collapsed = false;
35467         if(this.config.animate && skipAnim !== true){
35468             this.animateExpand();
35469         }else{
35470             this.el.show();
35471             if(this.split){
35472                 this.split.el.show();
35473             }
35474             this.collapsedEl.setLocation(-2000,-2000);
35475             this.collapsedEl.hide();
35476             this.fireEvent("invalidated", this);
35477             this.fireEvent("expanded", this);
35478         }
35479     },
35480 */
35481     animateExpand : function(){
35482         // overridden
35483     },
35484
35485     initTabs : function()
35486     {
35487         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35488         
35489         var ts = new Roo.bootstrap.panel.Tabs({
35490                 el: this.bodyEl.dom,
35491                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35492                 disableTooltips: this.config.disableTabTips,
35493                 toolbar : this.config.toolbar
35494             });
35495         
35496         if(this.config.hideTabs){
35497             ts.stripWrap.setDisplayed(false);
35498         }
35499         this.tabs = ts;
35500         ts.resizeTabs = this.config.resizeTabs === true;
35501         ts.minTabWidth = this.config.minTabWidth || 40;
35502         ts.maxTabWidth = this.config.maxTabWidth || 250;
35503         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35504         ts.monitorResize = false;
35505         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35506         ts.bodyEl.addClass('roo-layout-tabs-body');
35507         this.panels.each(this.initPanelAsTab, this);
35508     },
35509
35510     initPanelAsTab : function(panel){
35511         var ti = this.tabs.addTab(
35512             panel.getEl().id,
35513             panel.getTitle(),
35514             null,
35515             this.config.closeOnTab && panel.isClosable(),
35516             panel.tpl
35517         );
35518         if(panel.tabTip !== undefined){
35519             ti.setTooltip(panel.tabTip);
35520         }
35521         ti.on("activate", function(){
35522               this.setActivePanel(panel);
35523         }, this);
35524         
35525         if(this.config.closeOnTab){
35526             ti.on("beforeclose", function(t, e){
35527                 e.cancel = true;
35528                 this.remove(panel);
35529             }, this);
35530         }
35531         
35532         panel.tabItem = ti;
35533         
35534         return ti;
35535     },
35536
35537     updatePanelTitle : function(panel, title)
35538     {
35539         if(this.activePanel == panel){
35540             this.updateTitle(title);
35541         }
35542         if(this.tabs){
35543             var ti = this.tabs.getTab(panel.getEl().id);
35544             ti.setText(title);
35545             if(panel.tabTip !== undefined){
35546                 ti.setTooltip(panel.tabTip);
35547             }
35548         }
35549     },
35550
35551     updateTitle : function(title){
35552         if(this.titleTextEl && !this.config.title){
35553             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35554         }
35555     },
35556
35557     setActivePanel : function(panel)
35558     {
35559         panel = this.getPanel(panel);
35560         if(this.activePanel && this.activePanel != panel){
35561             if(this.activePanel.setActiveState(false) === false){
35562                 return;
35563             }
35564         }
35565         this.activePanel = panel;
35566         panel.setActiveState(true);
35567         if(this.panelSize){
35568             panel.setSize(this.panelSize.width, this.panelSize.height);
35569         }
35570         if(this.closeBtn){
35571             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35572         }
35573         this.updateTitle(panel.getTitle());
35574         if(this.tabs){
35575             this.fireEvent("invalidated", this);
35576         }
35577         this.fireEvent("panelactivated", this, panel);
35578     },
35579
35580     /**
35581      * Shows the specified panel.
35582      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35583      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35584      */
35585     showPanel : function(panel)
35586     {
35587         panel = this.getPanel(panel);
35588         if(panel){
35589             if(this.tabs){
35590                 var tab = this.tabs.getTab(panel.getEl().id);
35591                 if(tab.isHidden()){
35592                     this.tabs.unhideTab(tab.id);
35593                 }
35594                 tab.activate();
35595             }else{
35596                 this.setActivePanel(panel);
35597             }
35598         }
35599         return panel;
35600     },
35601
35602     /**
35603      * Get the active panel for this region.
35604      * @return {Roo.ContentPanel} The active panel or null
35605      */
35606     getActivePanel : function(){
35607         return this.activePanel;
35608     },
35609
35610     validateVisibility : function(){
35611         if(this.panels.getCount() < 1){
35612             this.updateTitle("&#160;");
35613             this.closeBtn.hide();
35614             this.hide();
35615         }else{
35616             if(!this.isVisible()){
35617                 this.show();
35618             }
35619         }
35620     },
35621
35622     /**
35623      * Adds the passed ContentPanel(s) to this region.
35624      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35625      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35626      */
35627     add : function(panel)
35628     {
35629         if(arguments.length > 1){
35630             for(var i = 0, len = arguments.length; i < len; i++) {
35631                 this.add(arguments[i]);
35632             }
35633             return null;
35634         }
35635         
35636         // if we have not been rendered yet, then we can not really do much of this..
35637         if (!this.bodyEl) {
35638             this.unrendered_panels.push(panel);
35639             return panel;
35640         }
35641         
35642         
35643         
35644         
35645         if(this.hasPanel(panel)){
35646             this.showPanel(panel);
35647             return panel;
35648         }
35649         panel.setRegion(this);
35650         this.panels.add(panel);
35651        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35652             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35653             // and hide them... ???
35654             this.bodyEl.dom.appendChild(panel.getEl().dom);
35655             if(panel.background !== true){
35656                 this.setActivePanel(panel);
35657             }
35658             this.fireEvent("paneladded", this, panel);
35659             return panel;
35660         }
35661         */
35662         if(!this.tabs){
35663             this.initTabs();
35664         }else{
35665             this.initPanelAsTab(panel);
35666         }
35667         
35668         
35669         if(panel.background !== true){
35670             this.tabs.activate(panel.getEl().id);
35671         }
35672         this.fireEvent("paneladded", this, panel);
35673         return panel;
35674     },
35675
35676     /**
35677      * Hides the tab for the specified panel.
35678      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35679      */
35680     hidePanel : function(panel){
35681         if(this.tabs && (panel = this.getPanel(panel))){
35682             this.tabs.hideTab(panel.getEl().id);
35683         }
35684     },
35685
35686     /**
35687      * Unhides the tab for a previously hidden panel.
35688      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35689      */
35690     unhidePanel : function(panel){
35691         if(this.tabs && (panel = this.getPanel(panel))){
35692             this.tabs.unhideTab(panel.getEl().id);
35693         }
35694     },
35695
35696     clearPanels : function(){
35697         while(this.panels.getCount() > 0){
35698              this.remove(this.panels.first());
35699         }
35700     },
35701
35702     /**
35703      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35705      * @param {Boolean} preservePanel Overrides the config preservePanel option
35706      * @return {Roo.ContentPanel} The panel that was removed
35707      */
35708     remove : function(panel, preservePanel)
35709     {
35710         panel = this.getPanel(panel);
35711         if(!panel){
35712             return null;
35713         }
35714         var e = {};
35715         this.fireEvent("beforeremove", this, panel, e);
35716         if(e.cancel === true){
35717             return null;
35718         }
35719         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35720         var panelId = panel.getId();
35721         this.panels.removeKey(panelId);
35722         if(preservePanel){
35723             document.body.appendChild(panel.getEl().dom);
35724         }
35725         if(this.tabs){
35726             this.tabs.removeTab(panel.getEl().id);
35727         }else if (!preservePanel){
35728             this.bodyEl.dom.removeChild(panel.getEl().dom);
35729         }
35730         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35731             var p = this.panels.first();
35732             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35733             tempEl.appendChild(p.getEl().dom);
35734             this.bodyEl.update("");
35735             this.bodyEl.dom.appendChild(p.getEl().dom);
35736             tempEl = null;
35737             this.updateTitle(p.getTitle());
35738             this.tabs = null;
35739             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35740             this.setActivePanel(p);
35741         }
35742         panel.setRegion(null);
35743         if(this.activePanel == panel){
35744             this.activePanel = null;
35745         }
35746         if(this.config.autoDestroy !== false && preservePanel !== true){
35747             try{panel.destroy();}catch(e){}
35748         }
35749         this.fireEvent("panelremoved", this, panel);
35750         return panel;
35751     },
35752
35753     /**
35754      * Returns the TabPanel component used by this region
35755      * @return {Roo.TabPanel}
35756      */
35757     getTabs : function(){
35758         return this.tabs;
35759     },
35760
35761     createTool : function(parentEl, className){
35762         var btn = Roo.DomHelper.append(parentEl, {
35763             tag: "div",
35764             cls: "x-layout-tools-button",
35765             children: [ {
35766                 tag: "div",
35767                 cls: "roo-layout-tools-button-inner " + className,
35768                 html: "&#160;"
35769             }]
35770         }, true);
35771         btn.addClassOnOver("roo-layout-tools-button-over");
35772         return btn;
35773     }
35774 });/*
35775  * Based on:
35776  * Ext JS Library 1.1.1
35777  * Copyright(c) 2006-2007, Ext JS, LLC.
35778  *
35779  * Originally Released Under LGPL - original licence link has changed is not relivant.
35780  *
35781  * Fork - LGPL
35782  * <script type="text/javascript">
35783  */
35784  
35785
35786
35787 /**
35788  * @class Roo.SplitLayoutRegion
35789  * @extends Roo.LayoutRegion
35790  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35791  */
35792 Roo.bootstrap.layout.Split = function(config){
35793     this.cursor = config.cursor;
35794     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35795 };
35796
35797 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35798 {
35799     splitTip : "Drag to resize.",
35800     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35801     useSplitTips : false,
35802
35803     applyConfig : function(config){
35804         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35805     },
35806     
35807     onRender : function(ctr,pos) {
35808         
35809         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35810         if(!this.config.split){
35811             return;
35812         }
35813         if(!this.split){
35814             
35815             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35816                             tag: "div",
35817                             id: this.el.id + "-split",
35818                             cls: "roo-layout-split roo-layout-split-"+this.position,
35819                             html: "&#160;"
35820             });
35821             /** The SplitBar for this region 
35822             * @type Roo.SplitBar */
35823             // does not exist yet...
35824             Roo.log([this.position, this.orientation]);
35825             
35826             this.split = new Roo.bootstrap.SplitBar({
35827                 dragElement : splitEl,
35828                 resizingElement: this.el,
35829                 orientation : this.orientation
35830             });
35831             
35832             this.split.on("moved", this.onSplitMove, this);
35833             this.split.useShim = this.config.useShim === true;
35834             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35835             if(this.useSplitTips){
35836                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35837             }
35838             //if(config.collapsible){
35839             //    this.split.el.on("dblclick", this.collapse,  this);
35840             //}
35841         }
35842         if(typeof this.config.minSize != "undefined"){
35843             this.split.minSize = this.config.minSize;
35844         }
35845         if(typeof this.config.maxSize != "undefined"){
35846             this.split.maxSize = this.config.maxSize;
35847         }
35848         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35849             this.hideSplitter();
35850         }
35851         
35852     },
35853
35854     getHMaxSize : function(){
35855          var cmax = this.config.maxSize || 10000;
35856          var center = this.mgr.getRegion("center");
35857          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35858     },
35859
35860     getVMaxSize : function(){
35861          var cmax = this.config.maxSize || 10000;
35862          var center = this.mgr.getRegion("center");
35863          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35864     },
35865
35866     onSplitMove : function(split, newSize){
35867         this.fireEvent("resized", this, newSize);
35868     },
35869     
35870     /** 
35871      * Returns the {@link Roo.SplitBar} for this region.
35872      * @return {Roo.SplitBar}
35873      */
35874     getSplitBar : function(){
35875         return this.split;
35876     },
35877     
35878     hide : function(){
35879         this.hideSplitter();
35880         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35881     },
35882
35883     hideSplitter : function(){
35884         if(this.split){
35885             this.split.el.setLocation(-2000,-2000);
35886             this.split.el.hide();
35887         }
35888     },
35889
35890     show : function(){
35891         if(this.split){
35892             this.split.el.show();
35893         }
35894         Roo.bootstrap.layout.Split.superclass.show.call(this);
35895     },
35896     
35897     beforeSlide: function(){
35898         if(Roo.isGecko){// firefox overflow auto bug workaround
35899             this.bodyEl.clip();
35900             if(this.tabs) {
35901                 this.tabs.bodyEl.clip();
35902             }
35903             if(this.activePanel){
35904                 this.activePanel.getEl().clip();
35905                 
35906                 if(this.activePanel.beforeSlide){
35907                     this.activePanel.beforeSlide();
35908                 }
35909             }
35910         }
35911     },
35912     
35913     afterSlide : function(){
35914         if(Roo.isGecko){// firefox overflow auto bug workaround
35915             this.bodyEl.unclip();
35916             if(this.tabs) {
35917                 this.tabs.bodyEl.unclip();
35918             }
35919             if(this.activePanel){
35920                 this.activePanel.getEl().unclip();
35921                 if(this.activePanel.afterSlide){
35922                     this.activePanel.afterSlide();
35923                 }
35924             }
35925         }
35926     },
35927
35928     initAutoHide : function(){
35929         if(this.autoHide !== false){
35930             if(!this.autoHideHd){
35931                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35932                 this.autoHideHd = {
35933                     "mouseout": function(e){
35934                         if(!e.within(this.el, true)){
35935                             st.delay(500);
35936                         }
35937                     },
35938                     "mouseover" : function(e){
35939                         st.cancel();
35940                     },
35941                     scope : this
35942                 };
35943             }
35944             this.el.on(this.autoHideHd);
35945         }
35946     },
35947
35948     clearAutoHide : function(){
35949         if(this.autoHide !== false){
35950             this.el.un("mouseout", this.autoHideHd.mouseout);
35951             this.el.un("mouseover", this.autoHideHd.mouseover);
35952         }
35953     },
35954
35955     clearMonitor : function(){
35956         Roo.get(document).un("click", this.slideInIf, this);
35957     },
35958
35959     // these names are backwards but not changed for compat
35960     slideOut : function(){
35961         if(this.isSlid || this.el.hasActiveFx()){
35962             return;
35963         }
35964         this.isSlid = true;
35965         if(this.collapseBtn){
35966             this.collapseBtn.hide();
35967         }
35968         this.closeBtnState = this.closeBtn.getStyle('display');
35969         this.closeBtn.hide();
35970         if(this.stickBtn){
35971             this.stickBtn.show();
35972         }
35973         this.el.show();
35974         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35975         this.beforeSlide();
35976         this.el.setStyle("z-index", 10001);
35977         this.el.slideIn(this.getSlideAnchor(), {
35978             callback: function(){
35979                 this.afterSlide();
35980                 this.initAutoHide();
35981                 Roo.get(document).on("click", this.slideInIf, this);
35982                 this.fireEvent("slideshow", this);
35983             },
35984             scope: this,
35985             block: true
35986         });
35987     },
35988
35989     afterSlideIn : function(){
35990         this.clearAutoHide();
35991         this.isSlid = false;
35992         this.clearMonitor();
35993         this.el.setStyle("z-index", "");
35994         if(this.collapseBtn){
35995             this.collapseBtn.show();
35996         }
35997         this.closeBtn.setStyle('display', this.closeBtnState);
35998         if(this.stickBtn){
35999             this.stickBtn.hide();
36000         }
36001         this.fireEvent("slidehide", this);
36002     },
36003
36004     slideIn : function(cb){
36005         if(!this.isSlid || this.el.hasActiveFx()){
36006             Roo.callback(cb);
36007             return;
36008         }
36009         this.isSlid = false;
36010         this.beforeSlide();
36011         this.el.slideOut(this.getSlideAnchor(), {
36012             callback: function(){
36013                 this.el.setLeftTop(-10000, -10000);
36014                 this.afterSlide();
36015                 this.afterSlideIn();
36016                 Roo.callback(cb);
36017             },
36018             scope: this,
36019             block: true
36020         });
36021     },
36022     
36023     slideInIf : function(e){
36024         if(!e.within(this.el)){
36025             this.slideIn();
36026         }
36027     },
36028
36029     animateCollapse : function(){
36030         this.beforeSlide();
36031         this.el.setStyle("z-index", 20000);
36032         var anchor = this.getSlideAnchor();
36033         this.el.slideOut(anchor, {
36034             callback : function(){
36035                 this.el.setStyle("z-index", "");
36036                 this.collapsedEl.slideIn(anchor, {duration:.3});
36037                 this.afterSlide();
36038                 this.el.setLocation(-10000,-10000);
36039                 this.el.hide();
36040                 this.fireEvent("collapsed", this);
36041             },
36042             scope: this,
36043             block: true
36044         });
36045     },
36046
36047     animateExpand : function(){
36048         this.beforeSlide();
36049         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36050         this.el.setStyle("z-index", 20000);
36051         this.collapsedEl.hide({
36052             duration:.1
36053         });
36054         this.el.slideIn(this.getSlideAnchor(), {
36055             callback : function(){
36056                 this.el.setStyle("z-index", "");
36057                 this.afterSlide();
36058                 if(this.split){
36059                     this.split.el.show();
36060                 }
36061                 this.fireEvent("invalidated", this);
36062                 this.fireEvent("expanded", this);
36063             },
36064             scope: this,
36065             block: true
36066         });
36067     },
36068
36069     anchors : {
36070         "west" : "left",
36071         "east" : "right",
36072         "north" : "top",
36073         "south" : "bottom"
36074     },
36075
36076     sanchors : {
36077         "west" : "l",
36078         "east" : "r",
36079         "north" : "t",
36080         "south" : "b"
36081     },
36082
36083     canchors : {
36084         "west" : "tl-tr",
36085         "east" : "tr-tl",
36086         "north" : "tl-bl",
36087         "south" : "bl-tl"
36088     },
36089
36090     getAnchor : function(){
36091         return this.anchors[this.position];
36092     },
36093
36094     getCollapseAnchor : function(){
36095         return this.canchors[this.position];
36096     },
36097
36098     getSlideAnchor : function(){
36099         return this.sanchors[this.position];
36100     },
36101
36102     getAlignAdj : function(){
36103         var cm = this.cmargins;
36104         switch(this.position){
36105             case "west":
36106                 return [0, 0];
36107             break;
36108             case "east":
36109                 return [0, 0];
36110             break;
36111             case "north":
36112                 return [0, 0];
36113             break;
36114             case "south":
36115                 return [0, 0];
36116             break;
36117         }
36118     },
36119
36120     getExpandAdj : function(){
36121         var c = this.collapsedEl, cm = this.cmargins;
36122         switch(this.position){
36123             case "west":
36124                 return [-(cm.right+c.getWidth()+cm.left), 0];
36125             break;
36126             case "east":
36127                 return [cm.right+c.getWidth()+cm.left, 0];
36128             break;
36129             case "north":
36130                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36131             break;
36132             case "south":
36133                 return [0, cm.top+cm.bottom+c.getHeight()];
36134             break;
36135         }
36136     }
36137 });/*
36138  * Based on:
36139  * Ext JS Library 1.1.1
36140  * Copyright(c) 2006-2007, Ext JS, LLC.
36141  *
36142  * Originally Released Under LGPL - original licence link has changed is not relivant.
36143  *
36144  * Fork - LGPL
36145  * <script type="text/javascript">
36146  */
36147 /*
36148  * These classes are private internal classes
36149  */
36150 Roo.bootstrap.layout.Center = function(config){
36151     config.region = "center";
36152     Roo.bootstrap.layout.Region.call(this, config);
36153     this.visible = true;
36154     this.minWidth = config.minWidth || 20;
36155     this.minHeight = config.minHeight || 20;
36156 };
36157
36158 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36159     hide : function(){
36160         // center panel can't be hidden
36161     },
36162     
36163     show : function(){
36164         // center panel can't be hidden
36165     },
36166     
36167     getMinWidth: function(){
36168         return this.minWidth;
36169     },
36170     
36171     getMinHeight: function(){
36172         return this.minHeight;
36173     }
36174 });
36175
36176
36177
36178
36179  
36180
36181
36182
36183
36184
36185 Roo.bootstrap.layout.North = function(config)
36186 {
36187     config.region = 'north';
36188     config.cursor = 'n-resize';
36189     
36190     Roo.bootstrap.layout.Split.call(this, config);
36191     
36192     
36193     if(this.split){
36194         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36195         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36196         this.split.el.addClass("roo-layout-split-v");
36197     }
36198     var size = config.initialSize || config.height;
36199     if(typeof size != "undefined"){
36200         this.el.setHeight(size);
36201     }
36202 };
36203 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36204 {
36205     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36206     
36207     
36208     
36209     getBox : function(){
36210         if(this.collapsed){
36211             return this.collapsedEl.getBox();
36212         }
36213         var box = this.el.getBox();
36214         if(this.split){
36215             box.height += this.split.el.getHeight();
36216         }
36217         return box;
36218     },
36219     
36220     updateBox : function(box){
36221         if(this.split && !this.collapsed){
36222             box.height -= this.split.el.getHeight();
36223             this.split.el.setLeft(box.x);
36224             this.split.el.setTop(box.y+box.height);
36225             this.split.el.setWidth(box.width);
36226         }
36227         if(this.collapsed){
36228             this.updateBody(box.width, null);
36229         }
36230         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36231     }
36232 });
36233
36234
36235
36236
36237
36238 Roo.bootstrap.layout.South = function(config){
36239     config.region = 'south';
36240     config.cursor = 's-resize';
36241     Roo.bootstrap.layout.Split.call(this, config);
36242     if(this.split){
36243         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36244         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36245         this.split.el.addClass("roo-layout-split-v");
36246     }
36247     var size = config.initialSize || config.height;
36248     if(typeof size != "undefined"){
36249         this.el.setHeight(size);
36250     }
36251 };
36252
36253 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36254     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36255     getBox : function(){
36256         if(this.collapsed){
36257             return this.collapsedEl.getBox();
36258         }
36259         var box = this.el.getBox();
36260         if(this.split){
36261             var sh = this.split.el.getHeight();
36262             box.height += sh;
36263             box.y -= sh;
36264         }
36265         return box;
36266     },
36267     
36268     updateBox : function(box){
36269         if(this.split && !this.collapsed){
36270             var sh = this.split.el.getHeight();
36271             box.height -= sh;
36272             box.y += sh;
36273             this.split.el.setLeft(box.x);
36274             this.split.el.setTop(box.y-sh);
36275             this.split.el.setWidth(box.width);
36276         }
36277         if(this.collapsed){
36278             this.updateBody(box.width, null);
36279         }
36280         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36281     }
36282 });
36283
36284 Roo.bootstrap.layout.East = function(config){
36285     config.region = "east";
36286     config.cursor = "e-resize";
36287     Roo.bootstrap.layout.Split.call(this, config);
36288     if(this.split){
36289         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36290         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36291         this.split.el.addClass("roo-layout-split-h");
36292     }
36293     var size = config.initialSize || config.width;
36294     if(typeof size != "undefined"){
36295         this.el.setWidth(size);
36296     }
36297 };
36298 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36299     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36300     getBox : function(){
36301         if(this.collapsed){
36302             return this.collapsedEl.getBox();
36303         }
36304         var box = this.el.getBox();
36305         if(this.split){
36306             var sw = this.split.el.getWidth();
36307             box.width += sw;
36308             box.x -= sw;
36309         }
36310         return box;
36311     },
36312
36313     updateBox : function(box){
36314         if(this.split && !this.collapsed){
36315             var sw = this.split.el.getWidth();
36316             box.width -= sw;
36317             this.split.el.setLeft(box.x);
36318             this.split.el.setTop(box.y);
36319             this.split.el.setHeight(box.height);
36320             box.x += sw;
36321         }
36322         if(this.collapsed){
36323             this.updateBody(null, box.height);
36324         }
36325         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36326     }
36327 });
36328
36329 Roo.bootstrap.layout.West = function(config){
36330     config.region = "west";
36331     config.cursor = "w-resize";
36332     
36333     Roo.bootstrap.layout.Split.call(this, config);
36334     if(this.split){
36335         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36336         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36337         this.split.el.addClass("roo-layout-split-h");
36338     }
36339     
36340 };
36341 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36342     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36343     
36344     onRender: function(ctr, pos)
36345     {
36346         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36347         var size = this.config.initialSize || this.config.width;
36348         if(typeof size != "undefined"){
36349             this.el.setWidth(size);
36350         }
36351     },
36352     
36353     getBox : function(){
36354         if(this.collapsed){
36355             return this.collapsedEl.getBox();
36356         }
36357         var box = this.el.getBox();
36358         if(this.split){
36359             box.width += this.split.el.getWidth();
36360         }
36361         return box;
36362     },
36363     
36364     updateBox : function(box){
36365         if(this.split && !this.collapsed){
36366             var sw = this.split.el.getWidth();
36367             box.width -= sw;
36368             this.split.el.setLeft(box.x+box.width);
36369             this.split.el.setTop(box.y);
36370             this.split.el.setHeight(box.height);
36371         }
36372         if(this.collapsed){
36373             this.updateBody(null, box.height);
36374         }
36375         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36376     }
36377 });
36378 Roo.namespace("Roo.bootstrap.panel");/*
36379  * Based on:
36380  * Ext JS Library 1.1.1
36381  * Copyright(c) 2006-2007, Ext JS, LLC.
36382  *
36383  * Originally Released Under LGPL - original licence link has changed is not relivant.
36384  *
36385  * Fork - LGPL
36386  * <script type="text/javascript">
36387  */
36388 /**
36389  * @class Roo.ContentPanel
36390  * @extends Roo.util.Observable
36391  * A basic ContentPanel element.
36392  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36393  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36394  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36395  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36396  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36397  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36398  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36399  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36400  * @cfg {String} title          The title for this panel
36401  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36402  * @cfg {String} url            Calls {@link #setUrl} with this value
36403  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36404  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36405  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36406  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36407  * @cfg {Boolean} badges render the badges
36408
36409  * @constructor
36410  * Create a new ContentPanel.
36411  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36412  * @param {String/Object} config A string to set only the title or a config object
36413  * @param {String} content (optional) Set the HTML content for this panel
36414  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36415  */
36416 Roo.bootstrap.panel.Content = function( config){
36417     
36418     this.tpl = config.tpl || false;
36419     
36420     var el = config.el;
36421     var content = config.content;
36422
36423     if(config.autoCreate){ // xtype is available if this is called from factory
36424         el = Roo.id();
36425     }
36426     this.el = Roo.get(el);
36427     if(!this.el && config && config.autoCreate){
36428         if(typeof config.autoCreate == "object"){
36429             if(!config.autoCreate.id){
36430                 config.autoCreate.id = config.id||el;
36431             }
36432             this.el = Roo.DomHelper.append(document.body,
36433                         config.autoCreate, true);
36434         }else{
36435             var elcfg =  {   tag: "div",
36436                             cls: "roo-layout-inactive-content",
36437                             id: config.id||el
36438                             };
36439             if (config.html) {
36440                 elcfg.html = config.html;
36441                 
36442             }
36443                         
36444             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36445         }
36446     } 
36447     this.closable = false;
36448     this.loaded = false;
36449     this.active = false;
36450    
36451       
36452     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36453         
36454         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36455         
36456         this.wrapEl = this.el; //this.el.wrap();
36457         var ti = [];
36458         if (config.toolbar.items) {
36459             ti = config.toolbar.items ;
36460             delete config.toolbar.items ;
36461         }
36462         
36463         var nitems = [];
36464         this.toolbar.render(this.wrapEl, 'before');
36465         for(var i =0;i < ti.length;i++) {
36466           //  Roo.log(['add child', items[i]]);
36467             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36468         }
36469         this.toolbar.items = nitems;
36470         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36471         delete config.toolbar;
36472         
36473     }
36474     /*
36475     // xtype created footer. - not sure if will work as we normally have to render first..
36476     if (this.footer && !this.footer.el && this.footer.xtype) {
36477         if (!this.wrapEl) {
36478             this.wrapEl = this.el.wrap();
36479         }
36480     
36481         this.footer.container = this.wrapEl.createChild();
36482          
36483         this.footer = Roo.factory(this.footer, Roo);
36484         
36485     }
36486     */
36487     
36488      if(typeof config == "string"){
36489         this.title = config;
36490     }else{
36491         Roo.apply(this, config);
36492     }
36493     
36494     if(this.resizeEl){
36495         this.resizeEl = Roo.get(this.resizeEl, true);
36496     }else{
36497         this.resizeEl = this.el;
36498     }
36499     // handle view.xtype
36500     
36501  
36502     
36503     
36504     this.addEvents({
36505         /**
36506          * @event activate
36507          * Fires when this panel is activated. 
36508          * @param {Roo.ContentPanel} this
36509          */
36510         "activate" : true,
36511         /**
36512          * @event deactivate
36513          * Fires when this panel is activated. 
36514          * @param {Roo.ContentPanel} this
36515          */
36516         "deactivate" : true,
36517
36518         /**
36519          * @event resize
36520          * Fires when this panel is resized if fitToFrame is true.
36521          * @param {Roo.ContentPanel} this
36522          * @param {Number} width The width after any component adjustments
36523          * @param {Number} height The height after any component adjustments
36524          */
36525         "resize" : true,
36526         
36527          /**
36528          * @event render
36529          * Fires when this tab is created
36530          * @param {Roo.ContentPanel} this
36531          */
36532         "render" : true
36533         
36534         
36535         
36536     });
36537     
36538
36539     
36540     
36541     if(this.autoScroll){
36542         this.resizeEl.setStyle("overflow", "auto");
36543     } else {
36544         // fix randome scrolling
36545         //this.el.on('scroll', function() {
36546         //    Roo.log('fix random scolling');
36547         //    this.scrollTo('top',0); 
36548         //});
36549     }
36550     content = content || this.content;
36551     if(content){
36552         this.setContent(content);
36553     }
36554     if(config && config.url){
36555         this.setUrl(this.url, this.params, this.loadOnce);
36556     }
36557     
36558     
36559     
36560     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36561     
36562     if (this.view && typeof(this.view.xtype) != 'undefined') {
36563         this.view.el = this.el.appendChild(document.createElement("div"));
36564         this.view = Roo.factory(this.view); 
36565         this.view.render  &&  this.view.render(false, '');  
36566     }
36567     
36568     
36569     this.fireEvent('render', this);
36570 };
36571
36572 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36573     
36574     tabTip : '',
36575     
36576     setRegion : function(region){
36577         this.region = region;
36578         this.setActiveClass(region && !this.background);
36579     },
36580     
36581     
36582     setActiveClass: function(state)
36583     {
36584         if(state){
36585            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36586            this.el.setStyle('position','relative');
36587         }else{
36588            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36589            this.el.setStyle('position', 'absolute');
36590         } 
36591     },
36592     
36593     /**
36594      * Returns the toolbar for this Panel if one was configured. 
36595      * @return {Roo.Toolbar} 
36596      */
36597     getToolbar : function(){
36598         return this.toolbar;
36599     },
36600     
36601     setActiveState : function(active)
36602     {
36603         this.active = active;
36604         this.setActiveClass(active);
36605         if(!active){
36606             if(this.fireEvent("deactivate", this) === false){
36607                 return false;
36608             }
36609             return true;
36610         }
36611         this.fireEvent("activate", this);
36612         return true;
36613     },
36614     /**
36615      * Updates this panel's element
36616      * @param {String} content The new content
36617      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36618     */
36619     setContent : function(content, loadScripts){
36620         this.el.update(content, loadScripts);
36621     },
36622
36623     ignoreResize : function(w, h){
36624         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36625             return true;
36626         }else{
36627             this.lastSize = {width: w, height: h};
36628             return false;
36629         }
36630     },
36631     /**
36632      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36633      * @return {Roo.UpdateManager} The UpdateManager
36634      */
36635     getUpdateManager : function(){
36636         return this.el.getUpdateManager();
36637     },
36638      /**
36639      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36640      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36641 <pre><code>
36642 panel.load({
36643     url: "your-url.php",
36644     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36645     callback: yourFunction,
36646     scope: yourObject, //(optional scope)
36647     discardUrl: false,
36648     nocache: false,
36649     text: "Loading...",
36650     timeout: 30,
36651     scripts: false
36652 });
36653 </code></pre>
36654      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36655      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36656      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36657      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36658      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36659      * @return {Roo.ContentPanel} this
36660      */
36661     load : function(){
36662         var um = this.el.getUpdateManager();
36663         um.update.apply(um, arguments);
36664         return this;
36665     },
36666
36667
36668     /**
36669      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36670      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36671      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36672      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36673      * @return {Roo.UpdateManager} The UpdateManager
36674      */
36675     setUrl : function(url, params, loadOnce){
36676         if(this.refreshDelegate){
36677             this.removeListener("activate", this.refreshDelegate);
36678         }
36679         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36680         this.on("activate", this.refreshDelegate);
36681         return this.el.getUpdateManager();
36682     },
36683     
36684     _handleRefresh : function(url, params, loadOnce){
36685         if(!loadOnce || !this.loaded){
36686             var updater = this.el.getUpdateManager();
36687             updater.update(url, params, this._setLoaded.createDelegate(this));
36688         }
36689     },
36690     
36691     _setLoaded : function(){
36692         this.loaded = true;
36693     }, 
36694     
36695     /**
36696      * Returns this panel's id
36697      * @return {String} 
36698      */
36699     getId : function(){
36700         return this.el.id;
36701     },
36702     
36703     /** 
36704      * Returns this panel's element - used by regiosn to add.
36705      * @return {Roo.Element} 
36706      */
36707     getEl : function(){
36708         return this.wrapEl || this.el;
36709     },
36710     
36711    
36712     
36713     adjustForComponents : function(width, height)
36714     {
36715         //Roo.log('adjustForComponents ');
36716         if(this.resizeEl != this.el){
36717             width -= this.el.getFrameWidth('lr');
36718             height -= this.el.getFrameWidth('tb');
36719         }
36720         if(this.toolbar){
36721             var te = this.toolbar.getEl();
36722             te.setWidth(width);
36723             height -= te.getHeight();
36724         }
36725         if(this.footer){
36726             var te = this.footer.getEl();
36727             te.setWidth(width);
36728             height -= te.getHeight();
36729         }
36730         
36731         
36732         if(this.adjustments){
36733             width += this.adjustments[0];
36734             height += this.adjustments[1];
36735         }
36736         return {"width": width, "height": height};
36737     },
36738     
36739     setSize : function(width, height){
36740         if(this.fitToFrame && !this.ignoreResize(width, height)){
36741             if(this.fitContainer && this.resizeEl != this.el){
36742                 this.el.setSize(width, height);
36743             }
36744             var size = this.adjustForComponents(width, height);
36745             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36746             this.fireEvent('resize', this, size.width, size.height);
36747         }
36748     },
36749     
36750     /**
36751      * Returns this panel's title
36752      * @return {String} 
36753      */
36754     getTitle : function(){
36755         
36756         if (typeof(this.title) != 'object') {
36757             return this.title;
36758         }
36759         
36760         var t = '';
36761         for (var k in this.title) {
36762             if (!this.title.hasOwnProperty(k)) {
36763                 continue;
36764             }
36765             
36766             if (k.indexOf('-') >= 0) {
36767                 var s = k.split('-');
36768                 for (var i = 0; i<s.length; i++) {
36769                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36770                 }
36771             } else {
36772                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36773             }
36774         }
36775         return t;
36776     },
36777     
36778     /**
36779      * Set this panel's title
36780      * @param {String} title
36781      */
36782     setTitle : function(title){
36783         this.title = title;
36784         if(this.region){
36785             this.region.updatePanelTitle(this, title);
36786         }
36787     },
36788     
36789     /**
36790      * Returns true is this panel was configured to be closable
36791      * @return {Boolean} 
36792      */
36793     isClosable : function(){
36794         return this.closable;
36795     },
36796     
36797     beforeSlide : function(){
36798         this.el.clip();
36799         this.resizeEl.clip();
36800     },
36801     
36802     afterSlide : function(){
36803         this.el.unclip();
36804         this.resizeEl.unclip();
36805     },
36806     
36807     /**
36808      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36809      *   Will fail silently if the {@link #setUrl} method has not been called.
36810      *   This does not activate the panel, just updates its content.
36811      */
36812     refresh : function(){
36813         if(this.refreshDelegate){
36814            this.loaded = false;
36815            this.refreshDelegate();
36816         }
36817     },
36818     
36819     /**
36820      * Destroys this panel
36821      */
36822     destroy : function(){
36823         this.el.removeAllListeners();
36824         var tempEl = document.createElement("span");
36825         tempEl.appendChild(this.el.dom);
36826         tempEl.innerHTML = "";
36827         this.el.remove();
36828         this.el = null;
36829     },
36830     
36831     /**
36832      * form - if the content panel contains a form - this is a reference to it.
36833      * @type {Roo.form.Form}
36834      */
36835     form : false,
36836     /**
36837      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36838      *    This contains a reference to it.
36839      * @type {Roo.View}
36840      */
36841     view : false,
36842     
36843       /**
36844      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36845      * <pre><code>
36846
36847 layout.addxtype({
36848        xtype : 'Form',
36849        items: [ .... ]
36850    }
36851 );
36852
36853 </code></pre>
36854      * @param {Object} cfg Xtype definition of item to add.
36855      */
36856     
36857     
36858     getChildContainer: function () {
36859         return this.getEl();
36860     }
36861     
36862     
36863     /*
36864         var  ret = new Roo.factory(cfg);
36865         return ret;
36866         
36867         
36868         // add form..
36869         if (cfg.xtype.match(/^Form$/)) {
36870             
36871             var el;
36872             //if (this.footer) {
36873             //    el = this.footer.container.insertSibling(false, 'before');
36874             //} else {
36875                 el = this.el.createChild();
36876             //}
36877
36878             this.form = new  Roo.form.Form(cfg);
36879             
36880             
36881             if ( this.form.allItems.length) {
36882                 this.form.render(el.dom);
36883             }
36884             return this.form;
36885         }
36886         // should only have one of theses..
36887         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36888             // views.. should not be just added - used named prop 'view''
36889             
36890             cfg.el = this.el.appendChild(document.createElement("div"));
36891             // factory?
36892             
36893             var ret = new Roo.factory(cfg);
36894              
36895              ret.render && ret.render(false, ''); // render blank..
36896             this.view = ret;
36897             return ret;
36898         }
36899         return false;
36900     }
36901     \*/
36902 });
36903  
36904 /**
36905  * @class Roo.bootstrap.panel.Grid
36906  * @extends Roo.bootstrap.panel.Content
36907  * @constructor
36908  * Create a new GridPanel.
36909  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36910  * @param {Object} config A the config object
36911   
36912  */
36913
36914
36915
36916 Roo.bootstrap.panel.Grid = function(config)
36917 {
36918     
36919       
36920     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36921         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36922
36923     config.el = this.wrapper;
36924     //this.el = this.wrapper;
36925     
36926       if (config.container) {
36927         // ctor'ed from a Border/panel.grid
36928         
36929         
36930         this.wrapper.setStyle("overflow", "hidden");
36931         this.wrapper.addClass('roo-grid-container');
36932
36933     }
36934     
36935     
36936     if(config.toolbar){
36937         var tool_el = this.wrapper.createChild();    
36938         this.toolbar = Roo.factory(config.toolbar);
36939         var ti = [];
36940         if (config.toolbar.items) {
36941             ti = config.toolbar.items ;
36942             delete config.toolbar.items ;
36943         }
36944         
36945         var nitems = [];
36946         this.toolbar.render(tool_el);
36947         for(var i =0;i < ti.length;i++) {
36948           //  Roo.log(['add child', items[i]]);
36949             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36950         }
36951         this.toolbar.items = nitems;
36952         
36953         delete config.toolbar;
36954     }
36955     
36956     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36957     config.grid.scrollBody = true;;
36958     config.grid.monitorWindowResize = false; // turn off autosizing
36959     config.grid.autoHeight = false;
36960     config.grid.autoWidth = false;
36961     
36962     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36963     
36964     if (config.background) {
36965         // render grid on panel activation (if panel background)
36966         this.on('activate', function(gp) {
36967             if (!gp.grid.rendered) {
36968                 gp.grid.render(this.wrapper);
36969                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36970             }
36971         });
36972             
36973     } else {
36974         this.grid.render(this.wrapper);
36975         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36976
36977     }
36978     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36979     // ??? needed ??? config.el = this.wrapper;
36980     
36981     
36982     
36983   
36984     // xtype created footer. - not sure if will work as we normally have to render first..
36985     if (this.footer && !this.footer.el && this.footer.xtype) {
36986         
36987         var ctr = this.grid.getView().getFooterPanel(true);
36988         this.footer.dataSource = this.grid.dataSource;
36989         this.footer = Roo.factory(this.footer, Roo);
36990         this.footer.render(ctr);
36991         
36992     }
36993     
36994     
36995     
36996     
36997      
36998 };
36999
37000 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37001     getId : function(){
37002         return this.grid.id;
37003     },
37004     
37005     /**
37006      * Returns the grid for this panel
37007      * @return {Roo.bootstrap.Table} 
37008      */
37009     getGrid : function(){
37010         return this.grid;    
37011     },
37012     
37013     setSize : function(width, height){
37014         if(!this.ignoreResize(width, height)){
37015             var grid = this.grid;
37016             var size = this.adjustForComponents(width, height);
37017             var gridel = grid.getGridEl();
37018             gridel.setSize(size.width, size.height);
37019             /*
37020             var thd = grid.getGridEl().select('thead',true).first();
37021             var tbd = grid.getGridEl().select('tbody', true).first();
37022             if (tbd) {
37023                 tbd.setSize(width, height - thd.getHeight());
37024             }
37025             */
37026             grid.autoSize();
37027         }
37028     },
37029      
37030     
37031     
37032     beforeSlide : function(){
37033         this.grid.getView().scroller.clip();
37034     },
37035     
37036     afterSlide : function(){
37037         this.grid.getView().scroller.unclip();
37038     },
37039     
37040     destroy : function(){
37041         this.grid.destroy();
37042         delete this.grid;
37043         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37044     }
37045 });
37046
37047 /**
37048  * @class Roo.bootstrap.panel.Nest
37049  * @extends Roo.bootstrap.panel.Content
37050  * @constructor
37051  * Create a new Panel, that can contain a layout.Border.
37052  * 
37053  * 
37054  * @param {Roo.BorderLayout} layout The layout for this panel
37055  * @param {String/Object} config A string to set only the title or a config object
37056  */
37057 Roo.bootstrap.panel.Nest = function(config)
37058 {
37059     // construct with only one argument..
37060     /* FIXME - implement nicer consturctors
37061     if (layout.layout) {
37062         config = layout;
37063         layout = config.layout;
37064         delete config.layout;
37065     }
37066     if (layout.xtype && !layout.getEl) {
37067         // then layout needs constructing..
37068         layout = Roo.factory(layout, Roo);
37069     }
37070     */
37071     
37072     config.el =  config.layout.getEl();
37073     
37074     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37075     
37076     config.layout.monitorWindowResize = false; // turn off autosizing
37077     this.layout = config.layout;
37078     this.layout.getEl().addClass("roo-layout-nested-layout");
37079     
37080     
37081     
37082     
37083 };
37084
37085 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37086
37087     setSize : function(width, height){
37088         if(!this.ignoreResize(width, height)){
37089             var size = this.adjustForComponents(width, height);
37090             var el = this.layout.getEl();
37091             if (size.height < 1) {
37092                 el.setWidth(size.width);   
37093             } else {
37094                 el.setSize(size.width, size.height);
37095             }
37096             var touch = el.dom.offsetWidth;
37097             this.layout.layout();
37098             // ie requires a double layout on the first pass
37099             if(Roo.isIE && !this.initialized){
37100                 this.initialized = true;
37101                 this.layout.layout();
37102             }
37103         }
37104     },
37105     
37106     // activate all subpanels if not currently active..
37107     
37108     setActiveState : function(active){
37109         this.active = active;
37110         this.setActiveClass(active);
37111         
37112         if(!active){
37113             this.fireEvent("deactivate", this);
37114             return;
37115         }
37116         
37117         this.fireEvent("activate", this);
37118         // not sure if this should happen before or after..
37119         if (!this.layout) {
37120             return; // should not happen..
37121         }
37122         var reg = false;
37123         for (var r in this.layout.regions) {
37124             reg = this.layout.getRegion(r);
37125             if (reg.getActivePanel()) {
37126                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37127                 reg.setActivePanel(reg.getActivePanel());
37128                 continue;
37129             }
37130             if (!reg.panels.length) {
37131                 continue;
37132             }
37133             reg.showPanel(reg.getPanel(0));
37134         }
37135         
37136         
37137         
37138         
37139     },
37140     
37141     /**
37142      * Returns the nested BorderLayout for this panel
37143      * @return {Roo.BorderLayout} 
37144      */
37145     getLayout : function(){
37146         return this.layout;
37147     },
37148     
37149      /**
37150      * Adds a xtype elements to the layout of the nested panel
37151      * <pre><code>
37152
37153 panel.addxtype({
37154        xtype : 'ContentPanel',
37155        region: 'west',
37156        items: [ .... ]
37157    }
37158 );
37159
37160 panel.addxtype({
37161         xtype : 'NestedLayoutPanel',
37162         region: 'west',
37163         layout: {
37164            center: { },
37165            west: { }   
37166         },
37167         items : [ ... list of content panels or nested layout panels.. ]
37168    }
37169 );
37170 </code></pre>
37171      * @param {Object} cfg Xtype definition of item to add.
37172      */
37173     addxtype : function(cfg) {
37174         return this.layout.addxtype(cfg);
37175     
37176     }
37177 });        /*
37178  * Based on:
37179  * Ext JS Library 1.1.1
37180  * Copyright(c) 2006-2007, Ext JS, LLC.
37181  *
37182  * Originally Released Under LGPL - original licence link has changed is not relivant.
37183  *
37184  * Fork - LGPL
37185  * <script type="text/javascript">
37186  */
37187 /**
37188  * @class Roo.TabPanel
37189  * @extends Roo.util.Observable
37190  * A lightweight tab container.
37191  * <br><br>
37192  * Usage:
37193  * <pre><code>
37194 // basic tabs 1, built from existing content
37195 var tabs = new Roo.TabPanel("tabs1");
37196 tabs.addTab("script", "View Script");
37197 tabs.addTab("markup", "View Markup");
37198 tabs.activate("script");
37199
37200 // more advanced tabs, built from javascript
37201 var jtabs = new Roo.TabPanel("jtabs");
37202 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37203
37204 // set up the UpdateManager
37205 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37206 var updater = tab2.getUpdateManager();
37207 updater.setDefaultUrl("ajax1.htm");
37208 tab2.on('activate', updater.refresh, updater, true);
37209
37210 // Use setUrl for Ajax loading
37211 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37212 tab3.setUrl("ajax2.htm", null, true);
37213
37214 // Disabled tab
37215 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37216 tab4.disable();
37217
37218 jtabs.activate("jtabs-1");
37219  * </code></pre>
37220  * @constructor
37221  * Create a new TabPanel.
37222  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37223  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37224  */
37225 Roo.bootstrap.panel.Tabs = function(config){
37226     /**
37227     * The container element for this TabPanel.
37228     * @type Roo.Element
37229     */
37230     this.el = Roo.get(config.el);
37231     delete config.el;
37232     if(config){
37233         if(typeof config == "boolean"){
37234             this.tabPosition = config ? "bottom" : "top";
37235         }else{
37236             Roo.apply(this, config);
37237         }
37238     }
37239     
37240     if(this.tabPosition == "bottom"){
37241         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37242         this.el.addClass("roo-tabs-bottom");
37243     }
37244     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37245     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37246     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37247     if(Roo.isIE){
37248         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37249     }
37250     if(this.tabPosition != "bottom"){
37251         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37252          * @type Roo.Element
37253          */
37254         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37255         this.el.addClass("roo-tabs-top");
37256     }
37257     this.items = [];
37258
37259     this.bodyEl.setStyle("position", "relative");
37260
37261     this.active = null;
37262     this.activateDelegate = this.activate.createDelegate(this);
37263
37264     this.addEvents({
37265         /**
37266          * @event tabchange
37267          * Fires when the active tab changes
37268          * @param {Roo.TabPanel} this
37269          * @param {Roo.TabPanelItem} activePanel The new active tab
37270          */
37271         "tabchange": true,
37272         /**
37273          * @event beforetabchange
37274          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37275          * @param {Roo.TabPanel} this
37276          * @param {Object} e Set cancel to true on this object to cancel the tab change
37277          * @param {Roo.TabPanelItem} tab The tab being changed to
37278          */
37279         "beforetabchange" : true
37280     });
37281
37282     Roo.EventManager.onWindowResize(this.onResize, this);
37283     this.cpad = this.el.getPadding("lr");
37284     this.hiddenCount = 0;
37285
37286
37287     // toolbar on the tabbar support...
37288     if (this.toolbar) {
37289         alert("no toolbar support yet");
37290         this.toolbar  = false;
37291         /*
37292         var tcfg = this.toolbar;
37293         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37294         this.toolbar = new Roo.Toolbar(tcfg);
37295         if (Roo.isSafari) {
37296             var tbl = tcfg.container.child('table', true);
37297             tbl.setAttribute('width', '100%');
37298         }
37299         */
37300         
37301     }
37302    
37303
37304
37305     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37306 };
37307
37308 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37309     /*
37310      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37311      */
37312     tabPosition : "top",
37313     /*
37314      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37315      */
37316     currentTabWidth : 0,
37317     /*
37318      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37319      */
37320     minTabWidth : 40,
37321     /*
37322      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37323      */
37324     maxTabWidth : 250,
37325     /*
37326      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37327      */
37328     preferredTabWidth : 175,
37329     /*
37330      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37331      */
37332     resizeTabs : false,
37333     /*
37334      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37335      */
37336     monitorResize : true,
37337     /*
37338      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37339      */
37340     toolbar : false,
37341
37342     /**
37343      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37344      * @param {String} id The id of the div to use <b>or create</b>
37345      * @param {String} text The text for the tab
37346      * @param {String} content (optional) Content to put in the TabPanelItem body
37347      * @param {Boolean} closable (optional) True to create a close icon on the tab
37348      * @return {Roo.TabPanelItem} The created TabPanelItem
37349      */
37350     addTab : function(id, text, content, closable, tpl)
37351     {
37352         var item = new Roo.bootstrap.panel.TabItem({
37353             panel: this,
37354             id : id,
37355             text : text,
37356             closable : closable,
37357             tpl : tpl
37358         });
37359         this.addTabItem(item);
37360         if(content){
37361             item.setContent(content);
37362         }
37363         return item;
37364     },
37365
37366     /**
37367      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37368      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37369      * @return {Roo.TabPanelItem}
37370      */
37371     getTab : function(id){
37372         return this.items[id];
37373     },
37374
37375     /**
37376      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37377      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37378      */
37379     hideTab : function(id){
37380         var t = this.items[id];
37381         if(!t.isHidden()){
37382            t.setHidden(true);
37383            this.hiddenCount++;
37384            this.autoSizeTabs();
37385         }
37386     },
37387
37388     /**
37389      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37390      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37391      */
37392     unhideTab : function(id){
37393         var t = this.items[id];
37394         if(t.isHidden()){
37395            t.setHidden(false);
37396            this.hiddenCount--;
37397            this.autoSizeTabs();
37398         }
37399     },
37400
37401     /**
37402      * Adds an existing {@link Roo.TabPanelItem}.
37403      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37404      */
37405     addTabItem : function(item){
37406         this.items[item.id] = item;
37407         this.items.push(item);
37408       //  if(this.resizeTabs){
37409     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37410   //         this.autoSizeTabs();
37411 //        }else{
37412 //            item.autoSize();
37413        // }
37414     },
37415
37416     /**
37417      * Removes a {@link Roo.TabPanelItem}.
37418      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37419      */
37420     removeTab : function(id){
37421         var items = this.items;
37422         var tab = items[id];
37423         if(!tab) { return; }
37424         var index = items.indexOf(tab);
37425         if(this.active == tab && items.length > 1){
37426             var newTab = this.getNextAvailable(index);
37427             if(newTab) {
37428                 newTab.activate();
37429             }
37430         }
37431         this.stripEl.dom.removeChild(tab.pnode.dom);
37432         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37433             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37434         }
37435         items.splice(index, 1);
37436         delete this.items[tab.id];
37437         tab.fireEvent("close", tab);
37438         tab.purgeListeners();
37439         this.autoSizeTabs();
37440     },
37441
37442     getNextAvailable : function(start){
37443         var items = this.items;
37444         var index = start;
37445         // look for a next tab that will slide over to
37446         // replace the one being removed
37447         while(index < items.length){
37448             var item = items[++index];
37449             if(item && !item.isHidden()){
37450                 return item;
37451             }
37452         }
37453         // if one isn't found select the previous tab (on the left)
37454         index = start;
37455         while(index >= 0){
37456             var item = items[--index];
37457             if(item && !item.isHidden()){
37458                 return item;
37459             }
37460         }
37461         return null;
37462     },
37463
37464     /**
37465      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37466      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37467      */
37468     disableTab : function(id){
37469         var tab = this.items[id];
37470         if(tab && this.active != tab){
37471             tab.disable();
37472         }
37473     },
37474
37475     /**
37476      * Enables a {@link Roo.TabPanelItem} that is disabled.
37477      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37478      */
37479     enableTab : function(id){
37480         var tab = this.items[id];
37481         tab.enable();
37482     },
37483
37484     /**
37485      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37486      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37487      * @return {Roo.TabPanelItem} The TabPanelItem.
37488      */
37489     activate : function(id){
37490         var tab = this.items[id];
37491         if(!tab){
37492             return null;
37493         }
37494         if(tab == this.active || tab.disabled){
37495             return tab;
37496         }
37497         var e = {};
37498         this.fireEvent("beforetabchange", this, e, tab);
37499         if(e.cancel !== true && !tab.disabled){
37500             if(this.active){
37501                 this.active.hide();
37502             }
37503             this.active = this.items[id];
37504             this.active.show();
37505             this.fireEvent("tabchange", this, this.active);
37506         }
37507         return tab;
37508     },
37509
37510     /**
37511      * Gets the active {@link Roo.TabPanelItem}.
37512      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37513      */
37514     getActiveTab : function(){
37515         return this.active;
37516     },
37517
37518     /**
37519      * Updates the tab body element to fit the height of the container element
37520      * for overflow scrolling
37521      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37522      */
37523     syncHeight : function(targetHeight){
37524         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37525         var bm = this.bodyEl.getMargins();
37526         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37527         this.bodyEl.setHeight(newHeight);
37528         return newHeight;
37529     },
37530
37531     onResize : function(){
37532         if(this.monitorResize){
37533             this.autoSizeTabs();
37534         }
37535     },
37536
37537     /**
37538      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37539      */
37540     beginUpdate : function(){
37541         this.updating = true;
37542     },
37543
37544     /**
37545      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37546      */
37547     endUpdate : function(){
37548         this.updating = false;
37549         this.autoSizeTabs();
37550     },
37551
37552     /**
37553      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37554      */
37555     autoSizeTabs : function(){
37556         var count = this.items.length;
37557         var vcount = count - this.hiddenCount;
37558         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37559             return;
37560         }
37561         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37562         var availWidth = Math.floor(w / vcount);
37563         var b = this.stripBody;
37564         if(b.getWidth() > w){
37565             var tabs = this.items;
37566             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37567             if(availWidth < this.minTabWidth){
37568                 /*if(!this.sleft){    // incomplete scrolling code
37569                     this.createScrollButtons();
37570                 }
37571                 this.showScroll();
37572                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37573             }
37574         }else{
37575             if(this.currentTabWidth < this.preferredTabWidth){
37576                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37577             }
37578         }
37579     },
37580
37581     /**
37582      * Returns the number of tabs in this TabPanel.
37583      * @return {Number}
37584      */
37585      getCount : function(){
37586          return this.items.length;
37587      },
37588
37589     /**
37590      * Resizes all the tabs to the passed width
37591      * @param {Number} The new width
37592      */
37593     setTabWidth : function(width){
37594         this.currentTabWidth = width;
37595         for(var i = 0, len = this.items.length; i < len; i++) {
37596                 if(!this.items[i].isHidden()) {
37597                 this.items[i].setWidth(width);
37598             }
37599         }
37600     },
37601
37602     /**
37603      * Destroys this TabPanel
37604      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37605      */
37606     destroy : function(removeEl){
37607         Roo.EventManager.removeResizeListener(this.onResize, this);
37608         for(var i = 0, len = this.items.length; i < len; i++){
37609             this.items[i].purgeListeners();
37610         }
37611         if(removeEl === true){
37612             this.el.update("");
37613             this.el.remove();
37614         }
37615     },
37616     
37617     createStrip : function(container)
37618     {
37619         var strip = document.createElement("nav");
37620         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37621         container.appendChild(strip);
37622         return strip;
37623     },
37624     
37625     createStripList : function(strip)
37626     {
37627         // div wrapper for retard IE
37628         // returns the "tr" element.
37629         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37630         //'<div class="x-tabs-strip-wrap">'+
37631           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37632           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37633         return strip.firstChild; //.firstChild.firstChild.firstChild;
37634     },
37635     createBody : function(container)
37636     {
37637         var body = document.createElement("div");
37638         Roo.id(body, "tab-body");
37639         //Roo.fly(body).addClass("x-tabs-body");
37640         Roo.fly(body).addClass("tab-content");
37641         container.appendChild(body);
37642         return body;
37643     },
37644     createItemBody :function(bodyEl, id){
37645         var body = Roo.getDom(id);
37646         if(!body){
37647             body = document.createElement("div");
37648             body.id = id;
37649         }
37650         //Roo.fly(body).addClass("x-tabs-item-body");
37651         Roo.fly(body).addClass("tab-pane");
37652          bodyEl.insertBefore(body, bodyEl.firstChild);
37653         return body;
37654     },
37655     /** @private */
37656     createStripElements :  function(stripEl, text, closable, tpl)
37657     {
37658         var td = document.createElement("li"); // was td..
37659         
37660         
37661         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37662         
37663         
37664         stripEl.appendChild(td);
37665         /*if(closable){
37666             td.className = "x-tabs-closable";
37667             if(!this.closeTpl){
37668                 this.closeTpl = new Roo.Template(
37669                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37670                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37671                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37672                 );
37673             }
37674             var el = this.closeTpl.overwrite(td, {"text": text});
37675             var close = el.getElementsByTagName("div")[0];
37676             var inner = el.getElementsByTagName("em")[0];
37677             return {"el": el, "close": close, "inner": inner};
37678         } else {
37679         */
37680         // not sure what this is..
37681 //            if(!this.tabTpl){
37682                 //this.tabTpl = new Roo.Template(
37683                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37684                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37685                 //);
37686 //                this.tabTpl = new Roo.Template(
37687 //                   '<a href="#">' +
37688 //                   '<span unselectable="on"' +
37689 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37690 //                            ' >{text}</span></a>'
37691 //                );
37692 //                
37693 //            }
37694
37695
37696             var template = tpl || this.tabTpl || false;
37697             
37698             if(!template){
37699                 
37700                 template = new Roo.Template(
37701                    '<a href="#">' +
37702                    '<span unselectable="on"' +
37703                             (this.disableTooltips ? '' : ' title="{text}"') +
37704                             ' >{text}</span></a>'
37705                 );
37706             }
37707             
37708             switch (typeof(template)) {
37709                 case 'object' :
37710                     break;
37711                 case 'string' :
37712                     template = new Roo.Template(template);
37713                     break;
37714                 default :
37715                     break;
37716             }
37717             
37718             var el = template.overwrite(td, {"text": text});
37719             
37720             var inner = el.getElementsByTagName("span")[0];
37721             
37722             return {"el": el, "inner": inner};
37723             
37724     }
37725         
37726     
37727 });
37728
37729 /**
37730  * @class Roo.TabPanelItem
37731  * @extends Roo.util.Observable
37732  * Represents an individual item (tab plus body) in a TabPanel.
37733  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37734  * @param {String} id The id of this TabPanelItem
37735  * @param {String} text The text for the tab of this TabPanelItem
37736  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37737  */
37738 Roo.bootstrap.panel.TabItem = function(config){
37739     /**
37740      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37741      * @type Roo.TabPanel
37742      */
37743     this.tabPanel = config.panel;
37744     /**
37745      * The id for this TabPanelItem
37746      * @type String
37747      */
37748     this.id = config.id;
37749     /** @private */
37750     this.disabled = false;
37751     /** @private */
37752     this.text = config.text;
37753     /** @private */
37754     this.loaded = false;
37755     this.closable = config.closable;
37756
37757     /**
37758      * The body element for this TabPanelItem.
37759      * @type Roo.Element
37760      */
37761     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37762     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37763     this.bodyEl.setStyle("display", "block");
37764     this.bodyEl.setStyle("zoom", "1");
37765     //this.hideAction();
37766
37767     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37768     /** @private */
37769     this.el = Roo.get(els.el);
37770     this.inner = Roo.get(els.inner, true);
37771     this.textEl = Roo.get(this.el.dom.firstChild, true);
37772     this.pnode = Roo.get(els.el.parentNode, true);
37773 //    this.el.on("mousedown", this.onTabMouseDown, this);
37774     this.el.on("click", this.onTabClick, this);
37775     /** @private */
37776     if(config.closable){
37777         var c = Roo.get(els.close, true);
37778         c.dom.title = this.closeText;
37779         c.addClassOnOver("close-over");
37780         c.on("click", this.closeClick, this);
37781      }
37782
37783     this.addEvents({
37784          /**
37785          * @event activate
37786          * Fires when this tab becomes the active tab.
37787          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37788          * @param {Roo.TabPanelItem} this
37789          */
37790         "activate": true,
37791         /**
37792          * @event beforeclose
37793          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37794          * @param {Roo.TabPanelItem} this
37795          * @param {Object} e Set cancel to true on this object to cancel the close.
37796          */
37797         "beforeclose": true,
37798         /**
37799          * @event close
37800          * Fires when this tab is closed.
37801          * @param {Roo.TabPanelItem} this
37802          */
37803          "close": true,
37804         /**
37805          * @event deactivate
37806          * Fires when this tab is no longer the active tab.
37807          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37808          * @param {Roo.TabPanelItem} this
37809          */
37810          "deactivate" : true
37811     });
37812     this.hidden = false;
37813
37814     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37815 };
37816
37817 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37818            {
37819     purgeListeners : function(){
37820        Roo.util.Observable.prototype.purgeListeners.call(this);
37821        this.el.removeAllListeners();
37822     },
37823     /**
37824      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37825      */
37826     show : function(){
37827         this.pnode.addClass("active");
37828         this.showAction();
37829         if(Roo.isOpera){
37830             this.tabPanel.stripWrap.repaint();
37831         }
37832         this.fireEvent("activate", this.tabPanel, this);
37833     },
37834
37835     /**
37836      * Returns true if this tab is the active tab.
37837      * @return {Boolean}
37838      */
37839     isActive : function(){
37840         return this.tabPanel.getActiveTab() == this;
37841     },
37842
37843     /**
37844      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37845      */
37846     hide : function(){
37847         this.pnode.removeClass("active");
37848         this.hideAction();
37849         this.fireEvent("deactivate", this.tabPanel, this);
37850     },
37851
37852     hideAction : function(){
37853         this.bodyEl.hide();
37854         this.bodyEl.setStyle("position", "absolute");
37855         this.bodyEl.setLeft("-20000px");
37856         this.bodyEl.setTop("-20000px");
37857     },
37858
37859     showAction : function(){
37860         this.bodyEl.setStyle("position", "relative");
37861         this.bodyEl.setTop("");
37862         this.bodyEl.setLeft("");
37863         this.bodyEl.show();
37864     },
37865
37866     /**
37867      * Set the tooltip for the tab.
37868      * @param {String} tooltip The tab's tooltip
37869      */
37870     setTooltip : function(text){
37871         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37872             this.textEl.dom.qtip = text;
37873             this.textEl.dom.removeAttribute('title');
37874         }else{
37875             this.textEl.dom.title = text;
37876         }
37877     },
37878
37879     onTabClick : function(e){
37880         e.preventDefault();
37881         this.tabPanel.activate(this.id);
37882     },
37883
37884     onTabMouseDown : function(e){
37885         e.preventDefault();
37886         this.tabPanel.activate(this.id);
37887     },
37888 /*
37889     getWidth : function(){
37890         return this.inner.getWidth();
37891     },
37892
37893     setWidth : function(width){
37894         var iwidth = width - this.pnode.getPadding("lr");
37895         this.inner.setWidth(iwidth);
37896         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37897         this.pnode.setWidth(width);
37898     },
37899 */
37900     /**
37901      * Show or hide the tab
37902      * @param {Boolean} hidden True to hide or false to show.
37903      */
37904     setHidden : function(hidden){
37905         this.hidden = hidden;
37906         this.pnode.setStyle("display", hidden ? "none" : "");
37907     },
37908
37909     /**
37910      * Returns true if this tab is "hidden"
37911      * @return {Boolean}
37912      */
37913     isHidden : function(){
37914         return this.hidden;
37915     },
37916
37917     /**
37918      * Returns the text for this tab
37919      * @return {String}
37920      */
37921     getText : function(){
37922         return this.text;
37923     },
37924     /*
37925     autoSize : function(){
37926         //this.el.beginMeasure();
37927         this.textEl.setWidth(1);
37928         /*
37929          *  #2804 [new] Tabs in Roojs
37930          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37931          */
37932         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37933         //this.el.endMeasure();
37934     //},
37935
37936     /**
37937      * Sets the text for the tab (Note: this also sets the tooltip text)
37938      * @param {String} text The tab's text and tooltip
37939      */
37940     setText : function(text){
37941         this.text = text;
37942         this.textEl.update(text);
37943         this.setTooltip(text);
37944         //if(!this.tabPanel.resizeTabs){
37945         //    this.autoSize();
37946         //}
37947     },
37948     /**
37949      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37950      */
37951     activate : function(){
37952         this.tabPanel.activate(this.id);
37953     },
37954
37955     /**
37956      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37957      */
37958     disable : function(){
37959         if(this.tabPanel.active != this){
37960             this.disabled = true;
37961             this.pnode.addClass("disabled");
37962         }
37963     },
37964
37965     /**
37966      * Enables this TabPanelItem if it was previously disabled.
37967      */
37968     enable : function(){
37969         this.disabled = false;
37970         this.pnode.removeClass("disabled");
37971     },
37972
37973     /**
37974      * Sets the content for this TabPanelItem.
37975      * @param {String} content The content
37976      * @param {Boolean} loadScripts true to look for and load scripts
37977      */
37978     setContent : function(content, loadScripts){
37979         this.bodyEl.update(content, loadScripts);
37980     },
37981
37982     /**
37983      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37984      * @return {Roo.UpdateManager} The UpdateManager
37985      */
37986     getUpdateManager : function(){
37987         return this.bodyEl.getUpdateManager();
37988     },
37989
37990     /**
37991      * Set a URL to be used to load the content for this TabPanelItem.
37992      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37993      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37994      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
37995      * @return {Roo.UpdateManager} The UpdateManager
37996      */
37997     setUrl : function(url, params, loadOnce){
37998         if(this.refreshDelegate){
37999             this.un('activate', this.refreshDelegate);
38000         }
38001         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38002         this.on("activate", this.refreshDelegate);
38003         return this.bodyEl.getUpdateManager();
38004     },
38005
38006     /** @private */
38007     _handleRefresh : function(url, params, loadOnce){
38008         if(!loadOnce || !this.loaded){
38009             var updater = this.bodyEl.getUpdateManager();
38010             updater.update(url, params, this._setLoaded.createDelegate(this));
38011         }
38012     },
38013
38014     /**
38015      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38016      *   Will fail silently if the setUrl method has not been called.
38017      *   This does not activate the panel, just updates its content.
38018      */
38019     refresh : function(){
38020         if(this.refreshDelegate){
38021            this.loaded = false;
38022            this.refreshDelegate();
38023         }
38024     },
38025
38026     /** @private */
38027     _setLoaded : function(){
38028         this.loaded = true;
38029     },
38030
38031     /** @private */
38032     closeClick : function(e){
38033         var o = {};
38034         e.stopEvent();
38035         this.fireEvent("beforeclose", this, o);
38036         if(o.cancel !== true){
38037             this.tabPanel.removeTab(this.id);
38038         }
38039     },
38040     /**
38041      * The text displayed in the tooltip for the close icon.
38042      * @type String
38043      */
38044     closeText : "Close this tab"
38045 });
38046 /**
38047 *    This script refer to:
38048 *    Title: International Telephone Input
38049 *    Author: Jack O'Connor
38050 *    Code version:  v12.1.12
38051 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38052 **/
38053
38054 Roo.bootstrap.PhoneInputData = function() {
38055     var d = [
38056       [
38057         "Afghanistan (‫افغانستان‬‎)",
38058         "af",
38059         "93"
38060       ],
38061       [
38062         "Albania (Shqipëri)",
38063         "al",
38064         "355"
38065       ],
38066       [
38067         "Algeria (‫الجزائر‬‎)",
38068         "dz",
38069         "213"
38070       ],
38071       [
38072         "American Samoa",
38073         "as",
38074         "1684"
38075       ],
38076       [
38077         "Andorra",
38078         "ad",
38079         "376"
38080       ],
38081       [
38082         "Angola",
38083         "ao",
38084         "244"
38085       ],
38086       [
38087         "Anguilla",
38088         "ai",
38089         "1264"
38090       ],
38091       [
38092         "Antigua and Barbuda",
38093         "ag",
38094         "1268"
38095       ],
38096       [
38097         "Argentina",
38098         "ar",
38099         "54"
38100       ],
38101       [
38102         "Armenia (Հայաստան)",
38103         "am",
38104         "374"
38105       ],
38106       [
38107         "Aruba",
38108         "aw",
38109         "297"
38110       ],
38111       [
38112         "Australia",
38113         "au",
38114         "61",
38115         0
38116       ],
38117       [
38118         "Austria (Österreich)",
38119         "at",
38120         "43"
38121       ],
38122       [
38123         "Azerbaijan (Azərbaycan)",
38124         "az",
38125         "994"
38126       ],
38127       [
38128         "Bahamas",
38129         "bs",
38130         "1242"
38131       ],
38132       [
38133         "Bahrain (‫البحرين‬‎)",
38134         "bh",
38135         "973"
38136       ],
38137       [
38138         "Bangladesh (বাংলাদেশ)",
38139         "bd",
38140         "880"
38141       ],
38142       [
38143         "Barbados",
38144         "bb",
38145         "1246"
38146       ],
38147       [
38148         "Belarus (Беларусь)",
38149         "by",
38150         "375"
38151       ],
38152       [
38153         "Belgium (België)",
38154         "be",
38155         "32"
38156       ],
38157       [
38158         "Belize",
38159         "bz",
38160         "501"
38161       ],
38162       [
38163         "Benin (Bénin)",
38164         "bj",
38165         "229"
38166       ],
38167       [
38168         "Bermuda",
38169         "bm",
38170         "1441"
38171       ],
38172       [
38173         "Bhutan (འབྲུག)",
38174         "bt",
38175         "975"
38176       ],
38177       [
38178         "Bolivia",
38179         "bo",
38180         "591"
38181       ],
38182       [
38183         "Bosnia and Herzegovina (Босна и Херцеговина)",
38184         "ba",
38185         "387"
38186       ],
38187       [
38188         "Botswana",
38189         "bw",
38190         "267"
38191       ],
38192       [
38193         "Brazil (Brasil)",
38194         "br",
38195         "55"
38196       ],
38197       [
38198         "British Indian Ocean Territory",
38199         "io",
38200         "246"
38201       ],
38202       [
38203         "British Virgin Islands",
38204         "vg",
38205         "1284"
38206       ],
38207       [
38208         "Brunei",
38209         "bn",
38210         "673"
38211       ],
38212       [
38213         "Bulgaria (България)",
38214         "bg",
38215         "359"
38216       ],
38217       [
38218         "Burkina Faso",
38219         "bf",
38220         "226"
38221       ],
38222       [
38223         "Burundi (Uburundi)",
38224         "bi",
38225         "257"
38226       ],
38227       [
38228         "Cambodia (កម្ពុជា)",
38229         "kh",
38230         "855"
38231       ],
38232       [
38233         "Cameroon (Cameroun)",
38234         "cm",
38235         "237"
38236       ],
38237       [
38238         "Canada",
38239         "ca",
38240         "1",
38241         1,
38242         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38243       ],
38244       [
38245         "Cape Verde (Kabu Verdi)",
38246         "cv",
38247         "238"
38248       ],
38249       [
38250         "Caribbean Netherlands",
38251         "bq",
38252         "599",
38253         1
38254       ],
38255       [
38256         "Cayman Islands",
38257         "ky",
38258         "1345"
38259       ],
38260       [
38261         "Central African Republic (République centrafricaine)",
38262         "cf",
38263         "236"
38264       ],
38265       [
38266         "Chad (Tchad)",
38267         "td",
38268         "235"
38269       ],
38270       [
38271         "Chile",
38272         "cl",
38273         "56"
38274       ],
38275       [
38276         "China (中国)",
38277         "cn",
38278         "86"
38279       ],
38280       [
38281         "Christmas Island",
38282         "cx",
38283         "61",
38284         2
38285       ],
38286       [
38287         "Cocos (Keeling) Islands",
38288         "cc",
38289         "61",
38290         1
38291       ],
38292       [
38293         "Colombia",
38294         "co",
38295         "57"
38296       ],
38297       [
38298         "Comoros (‫جزر القمر‬‎)",
38299         "km",
38300         "269"
38301       ],
38302       [
38303         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38304         "cd",
38305         "243"
38306       ],
38307       [
38308         "Congo (Republic) (Congo-Brazzaville)",
38309         "cg",
38310         "242"
38311       ],
38312       [
38313         "Cook Islands",
38314         "ck",
38315         "682"
38316       ],
38317       [
38318         "Costa Rica",
38319         "cr",
38320         "506"
38321       ],
38322       [
38323         "Côte d’Ivoire",
38324         "ci",
38325         "225"
38326       ],
38327       [
38328         "Croatia (Hrvatska)",
38329         "hr",
38330         "385"
38331       ],
38332       [
38333         "Cuba",
38334         "cu",
38335         "53"
38336       ],
38337       [
38338         "Curaçao",
38339         "cw",
38340         "599",
38341         0
38342       ],
38343       [
38344         "Cyprus (Κύπρος)",
38345         "cy",
38346         "357"
38347       ],
38348       [
38349         "Czech Republic (Česká republika)",
38350         "cz",
38351         "420"
38352       ],
38353       [
38354         "Denmark (Danmark)",
38355         "dk",
38356         "45"
38357       ],
38358       [
38359         "Djibouti",
38360         "dj",
38361         "253"
38362       ],
38363       [
38364         "Dominica",
38365         "dm",
38366         "1767"
38367       ],
38368       [
38369         "Dominican Republic (República Dominicana)",
38370         "do",
38371         "1",
38372         2,
38373         ["809", "829", "849"]
38374       ],
38375       [
38376         "Ecuador",
38377         "ec",
38378         "593"
38379       ],
38380       [
38381         "Egypt (‫مصر‬‎)",
38382         "eg",
38383         "20"
38384       ],
38385       [
38386         "El Salvador",
38387         "sv",
38388         "503"
38389       ],
38390       [
38391         "Equatorial Guinea (Guinea Ecuatorial)",
38392         "gq",
38393         "240"
38394       ],
38395       [
38396         "Eritrea",
38397         "er",
38398         "291"
38399       ],
38400       [
38401         "Estonia (Eesti)",
38402         "ee",
38403         "372"
38404       ],
38405       [
38406         "Ethiopia",
38407         "et",
38408         "251"
38409       ],
38410       [
38411         "Falkland Islands (Islas Malvinas)",
38412         "fk",
38413         "500"
38414       ],
38415       [
38416         "Faroe Islands (Føroyar)",
38417         "fo",
38418         "298"
38419       ],
38420       [
38421         "Fiji",
38422         "fj",
38423         "679"
38424       ],
38425       [
38426         "Finland (Suomi)",
38427         "fi",
38428         "358",
38429         0
38430       ],
38431       [
38432         "France",
38433         "fr",
38434         "33"
38435       ],
38436       [
38437         "French Guiana (Guyane française)",
38438         "gf",
38439         "594"
38440       ],
38441       [
38442         "French Polynesia (Polynésie française)",
38443         "pf",
38444         "689"
38445       ],
38446       [
38447         "Gabon",
38448         "ga",
38449         "241"
38450       ],
38451       [
38452         "Gambia",
38453         "gm",
38454         "220"
38455       ],
38456       [
38457         "Georgia (საქართველო)",
38458         "ge",
38459         "995"
38460       ],
38461       [
38462         "Germany (Deutschland)",
38463         "de",
38464         "49"
38465       ],
38466       [
38467         "Ghana (Gaana)",
38468         "gh",
38469         "233"
38470       ],
38471       [
38472         "Gibraltar",
38473         "gi",
38474         "350"
38475       ],
38476       [
38477         "Greece (Ελλάδα)",
38478         "gr",
38479         "30"
38480       ],
38481       [
38482         "Greenland (Kalaallit Nunaat)",
38483         "gl",
38484         "299"
38485       ],
38486       [
38487         "Grenada",
38488         "gd",
38489         "1473"
38490       ],
38491       [
38492         "Guadeloupe",
38493         "gp",
38494         "590",
38495         0
38496       ],
38497       [
38498         "Guam",
38499         "gu",
38500         "1671"
38501       ],
38502       [
38503         "Guatemala",
38504         "gt",
38505         "502"
38506       ],
38507       [
38508         "Guernsey",
38509         "gg",
38510         "44",
38511         1
38512       ],
38513       [
38514         "Guinea (Guinée)",
38515         "gn",
38516         "224"
38517       ],
38518       [
38519         "Guinea-Bissau (Guiné Bissau)",
38520         "gw",
38521         "245"
38522       ],
38523       [
38524         "Guyana",
38525         "gy",
38526         "592"
38527       ],
38528       [
38529         "Haiti",
38530         "ht",
38531         "509"
38532       ],
38533       [
38534         "Honduras",
38535         "hn",
38536         "504"
38537       ],
38538       [
38539         "Hong Kong (香港)",
38540         "hk",
38541         "852"
38542       ],
38543       [
38544         "Hungary (Magyarország)",
38545         "hu",
38546         "36"
38547       ],
38548       [
38549         "Iceland (Ísland)",
38550         "is",
38551         "354"
38552       ],
38553       [
38554         "India (भारत)",
38555         "in",
38556         "91"
38557       ],
38558       [
38559         "Indonesia",
38560         "id",
38561         "62"
38562       ],
38563       [
38564         "Iran (‫ایران‬‎)",
38565         "ir",
38566         "98"
38567       ],
38568       [
38569         "Iraq (‫العراق‬‎)",
38570         "iq",
38571         "964"
38572       ],
38573       [
38574         "Ireland",
38575         "ie",
38576         "353"
38577       ],
38578       [
38579         "Isle of Man",
38580         "im",
38581         "44",
38582         2
38583       ],
38584       [
38585         "Israel (‫ישראל‬‎)",
38586         "il",
38587         "972"
38588       ],
38589       [
38590         "Italy (Italia)",
38591         "it",
38592         "39",
38593         0
38594       ],
38595       [
38596         "Jamaica",
38597         "jm",
38598         "1876"
38599       ],
38600       [
38601         "Japan (日本)",
38602         "jp",
38603         "81"
38604       ],
38605       [
38606         "Jersey",
38607         "je",
38608         "44",
38609         3
38610       ],
38611       [
38612         "Jordan (‫الأردن‬‎)",
38613         "jo",
38614         "962"
38615       ],
38616       [
38617         "Kazakhstan (Казахстан)",
38618         "kz",
38619         "7",
38620         1
38621       ],
38622       [
38623         "Kenya",
38624         "ke",
38625         "254"
38626       ],
38627       [
38628         "Kiribati",
38629         "ki",
38630         "686"
38631       ],
38632       [
38633         "Kosovo",
38634         "xk",
38635         "383"
38636       ],
38637       [
38638         "Kuwait (‫الكويت‬‎)",
38639         "kw",
38640         "965"
38641       ],
38642       [
38643         "Kyrgyzstan (Кыргызстан)",
38644         "kg",
38645         "996"
38646       ],
38647       [
38648         "Laos (ລາວ)",
38649         "la",
38650         "856"
38651       ],
38652       [
38653         "Latvia (Latvija)",
38654         "lv",
38655         "371"
38656       ],
38657       [
38658         "Lebanon (‫لبنان‬‎)",
38659         "lb",
38660         "961"
38661       ],
38662       [
38663         "Lesotho",
38664         "ls",
38665         "266"
38666       ],
38667       [
38668         "Liberia",
38669         "lr",
38670         "231"
38671       ],
38672       [
38673         "Libya (‫ليبيا‬‎)",
38674         "ly",
38675         "218"
38676       ],
38677       [
38678         "Liechtenstein",
38679         "li",
38680         "423"
38681       ],
38682       [
38683         "Lithuania (Lietuva)",
38684         "lt",
38685         "370"
38686       ],
38687       [
38688         "Luxembourg",
38689         "lu",
38690         "352"
38691       ],
38692       [
38693         "Macau (澳門)",
38694         "mo",
38695         "853"
38696       ],
38697       [
38698         "Macedonia (FYROM) (Македонија)",
38699         "mk",
38700         "389"
38701       ],
38702       [
38703         "Madagascar (Madagasikara)",
38704         "mg",
38705         "261"
38706       ],
38707       [
38708         "Malawi",
38709         "mw",
38710         "265"
38711       ],
38712       [
38713         "Malaysia",
38714         "my",
38715         "60"
38716       ],
38717       [
38718         "Maldives",
38719         "mv",
38720         "960"
38721       ],
38722       [
38723         "Mali",
38724         "ml",
38725         "223"
38726       ],
38727       [
38728         "Malta",
38729         "mt",
38730         "356"
38731       ],
38732       [
38733         "Marshall Islands",
38734         "mh",
38735         "692"
38736       ],
38737       [
38738         "Martinique",
38739         "mq",
38740         "596"
38741       ],
38742       [
38743         "Mauritania (‫موريتانيا‬‎)",
38744         "mr",
38745         "222"
38746       ],
38747       [
38748         "Mauritius (Moris)",
38749         "mu",
38750         "230"
38751       ],
38752       [
38753         "Mayotte",
38754         "yt",
38755         "262",
38756         1
38757       ],
38758       [
38759         "Mexico (México)",
38760         "mx",
38761         "52"
38762       ],
38763       [
38764         "Micronesia",
38765         "fm",
38766         "691"
38767       ],
38768       [
38769         "Moldova (Republica Moldova)",
38770         "md",
38771         "373"
38772       ],
38773       [
38774         "Monaco",
38775         "mc",
38776         "377"
38777       ],
38778       [
38779         "Mongolia (Монгол)",
38780         "mn",
38781         "976"
38782       ],
38783       [
38784         "Montenegro (Crna Gora)",
38785         "me",
38786         "382"
38787       ],
38788       [
38789         "Montserrat",
38790         "ms",
38791         "1664"
38792       ],
38793       [
38794         "Morocco (‫المغرب‬‎)",
38795         "ma",
38796         "212",
38797         0
38798       ],
38799       [
38800         "Mozambique (Moçambique)",
38801         "mz",
38802         "258"
38803       ],
38804       [
38805         "Myanmar (Burma) (မြန်မာ)",
38806         "mm",
38807         "95"
38808       ],
38809       [
38810         "Namibia (Namibië)",
38811         "na",
38812         "264"
38813       ],
38814       [
38815         "Nauru",
38816         "nr",
38817         "674"
38818       ],
38819       [
38820         "Nepal (नेपाल)",
38821         "np",
38822         "977"
38823       ],
38824       [
38825         "Netherlands (Nederland)",
38826         "nl",
38827         "31"
38828       ],
38829       [
38830         "New Caledonia (Nouvelle-Calédonie)",
38831         "nc",
38832         "687"
38833       ],
38834       [
38835         "New Zealand",
38836         "nz",
38837         "64"
38838       ],
38839       [
38840         "Nicaragua",
38841         "ni",
38842         "505"
38843       ],
38844       [
38845         "Niger (Nijar)",
38846         "ne",
38847         "227"
38848       ],
38849       [
38850         "Nigeria",
38851         "ng",
38852         "234"
38853       ],
38854       [
38855         "Niue",
38856         "nu",
38857         "683"
38858       ],
38859       [
38860         "Norfolk Island",
38861         "nf",
38862         "672"
38863       ],
38864       [
38865         "North Korea (조선 민주주의 인민 공화국)",
38866         "kp",
38867         "850"
38868       ],
38869       [
38870         "Northern Mariana Islands",
38871         "mp",
38872         "1670"
38873       ],
38874       [
38875         "Norway (Norge)",
38876         "no",
38877         "47",
38878         0
38879       ],
38880       [
38881         "Oman (‫عُمان‬‎)",
38882         "om",
38883         "968"
38884       ],
38885       [
38886         "Pakistan (‫پاکستان‬‎)",
38887         "pk",
38888         "92"
38889       ],
38890       [
38891         "Palau",
38892         "pw",
38893         "680"
38894       ],
38895       [
38896         "Palestine (‫فلسطين‬‎)",
38897         "ps",
38898         "970"
38899       ],
38900       [
38901         "Panama (Panamá)",
38902         "pa",
38903         "507"
38904       ],
38905       [
38906         "Papua New Guinea",
38907         "pg",
38908         "675"
38909       ],
38910       [
38911         "Paraguay",
38912         "py",
38913         "595"
38914       ],
38915       [
38916         "Peru (Perú)",
38917         "pe",
38918         "51"
38919       ],
38920       [
38921         "Philippines",
38922         "ph",
38923         "63"
38924       ],
38925       [
38926         "Poland (Polska)",
38927         "pl",
38928         "48"
38929       ],
38930       [
38931         "Portugal",
38932         "pt",
38933         "351"
38934       ],
38935       [
38936         "Puerto Rico",
38937         "pr",
38938         "1",
38939         3,
38940         ["787", "939"]
38941       ],
38942       [
38943         "Qatar (‫قطر‬‎)",
38944         "qa",
38945         "974"
38946       ],
38947       [
38948         "Réunion (La Réunion)",
38949         "re",
38950         "262",
38951         0
38952       ],
38953       [
38954         "Romania (România)",
38955         "ro",
38956         "40"
38957       ],
38958       [
38959         "Russia (Россия)",
38960         "ru",
38961         "7",
38962         0
38963       ],
38964       [
38965         "Rwanda",
38966         "rw",
38967         "250"
38968       ],
38969       [
38970         "Saint Barthélemy",
38971         "bl",
38972         "590",
38973         1
38974       ],
38975       [
38976         "Saint Helena",
38977         "sh",
38978         "290"
38979       ],
38980       [
38981         "Saint Kitts and Nevis",
38982         "kn",
38983         "1869"
38984       ],
38985       [
38986         "Saint Lucia",
38987         "lc",
38988         "1758"
38989       ],
38990       [
38991         "Saint Martin (Saint-Martin (partie française))",
38992         "mf",
38993         "590",
38994         2
38995       ],
38996       [
38997         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38998         "pm",
38999         "508"
39000       ],
39001       [
39002         "Saint Vincent and the Grenadines",
39003         "vc",
39004         "1784"
39005       ],
39006       [
39007         "Samoa",
39008         "ws",
39009         "685"
39010       ],
39011       [
39012         "San Marino",
39013         "sm",
39014         "378"
39015       ],
39016       [
39017         "São Tomé and Príncipe (São Tomé e Príncipe)",
39018         "st",
39019         "239"
39020       ],
39021       [
39022         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39023         "sa",
39024         "966"
39025       ],
39026       [
39027         "Senegal (Sénégal)",
39028         "sn",
39029         "221"
39030       ],
39031       [
39032         "Serbia (Србија)",
39033         "rs",
39034         "381"
39035       ],
39036       [
39037         "Seychelles",
39038         "sc",
39039         "248"
39040       ],
39041       [
39042         "Sierra Leone",
39043         "sl",
39044         "232"
39045       ],
39046       [
39047         "Singapore",
39048         "sg",
39049         "65"
39050       ],
39051       [
39052         "Sint Maarten",
39053         "sx",
39054         "1721"
39055       ],
39056       [
39057         "Slovakia (Slovensko)",
39058         "sk",
39059         "421"
39060       ],
39061       [
39062         "Slovenia (Slovenija)",
39063         "si",
39064         "386"
39065       ],
39066       [
39067         "Solomon Islands",
39068         "sb",
39069         "677"
39070       ],
39071       [
39072         "Somalia (Soomaaliya)",
39073         "so",
39074         "252"
39075       ],
39076       [
39077         "South Africa",
39078         "za",
39079         "27"
39080       ],
39081       [
39082         "South Korea (대한민국)",
39083         "kr",
39084         "82"
39085       ],
39086       [
39087         "South Sudan (‫جنوب السودان‬‎)",
39088         "ss",
39089         "211"
39090       ],
39091       [
39092         "Spain (España)",
39093         "es",
39094         "34"
39095       ],
39096       [
39097         "Sri Lanka (ශ්‍රී ලංකාව)",
39098         "lk",
39099         "94"
39100       ],
39101       [
39102         "Sudan (‫السودان‬‎)",
39103         "sd",
39104         "249"
39105       ],
39106       [
39107         "Suriname",
39108         "sr",
39109         "597"
39110       ],
39111       [
39112         "Svalbard and Jan Mayen",
39113         "sj",
39114         "47",
39115         1
39116       ],
39117       [
39118         "Swaziland",
39119         "sz",
39120         "268"
39121       ],
39122       [
39123         "Sweden (Sverige)",
39124         "se",
39125         "46"
39126       ],
39127       [
39128         "Switzerland (Schweiz)",
39129         "ch",
39130         "41"
39131       ],
39132       [
39133         "Syria (‫سوريا‬‎)",
39134         "sy",
39135         "963"
39136       ],
39137       [
39138         "Taiwan (台灣)",
39139         "tw",
39140         "886"
39141       ],
39142       [
39143         "Tajikistan",
39144         "tj",
39145         "992"
39146       ],
39147       [
39148         "Tanzania",
39149         "tz",
39150         "255"
39151       ],
39152       [
39153         "Thailand (ไทย)",
39154         "th",
39155         "66"
39156       ],
39157       [
39158         "Timor-Leste",
39159         "tl",
39160         "670"
39161       ],
39162       [
39163         "Togo",
39164         "tg",
39165         "228"
39166       ],
39167       [
39168         "Tokelau",
39169         "tk",
39170         "690"
39171       ],
39172       [
39173         "Tonga",
39174         "to",
39175         "676"
39176       ],
39177       [
39178         "Trinidad and Tobago",
39179         "tt",
39180         "1868"
39181       ],
39182       [
39183         "Tunisia (‫تونس‬‎)",
39184         "tn",
39185         "216"
39186       ],
39187       [
39188         "Turkey (Türkiye)",
39189         "tr",
39190         "90"
39191       ],
39192       [
39193         "Turkmenistan",
39194         "tm",
39195         "993"
39196       ],
39197       [
39198         "Turks and Caicos Islands",
39199         "tc",
39200         "1649"
39201       ],
39202       [
39203         "Tuvalu",
39204         "tv",
39205         "688"
39206       ],
39207       [
39208         "U.S. Virgin Islands",
39209         "vi",
39210         "1340"
39211       ],
39212       [
39213         "Uganda",
39214         "ug",
39215         "256"
39216       ],
39217       [
39218         "Ukraine (Україна)",
39219         "ua",
39220         "380"
39221       ],
39222       [
39223         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39224         "ae",
39225         "971"
39226       ],
39227       [
39228         "United Kingdom",
39229         "gb",
39230         "44",
39231         0
39232       ],
39233       [
39234         "United States",
39235         "us",
39236         "1",
39237         0
39238       ],
39239       [
39240         "Uruguay",
39241         "uy",
39242         "598"
39243       ],
39244       [
39245         "Uzbekistan (Oʻzbekiston)",
39246         "uz",
39247         "998"
39248       ],
39249       [
39250         "Vanuatu",
39251         "vu",
39252         "678"
39253       ],
39254       [
39255         "Vatican City (Città del Vaticano)",
39256         "va",
39257         "39",
39258         1
39259       ],
39260       [
39261         "Venezuela",
39262         "ve",
39263         "58"
39264       ],
39265       [
39266         "Vietnam (Việt Nam)",
39267         "vn",
39268         "84"
39269       ],
39270       [
39271         "Wallis and Futuna (Wallis-et-Futuna)",
39272         "wf",
39273         "681"
39274       ],
39275       [
39276         "Western Sahara (‫الصحراء الغربية‬‎)",
39277         "eh",
39278         "212",
39279         1
39280       ],
39281       [
39282         "Yemen (‫اليمن‬‎)",
39283         "ye",
39284         "967"
39285       ],
39286       [
39287         "Zambia",
39288         "zm",
39289         "260"
39290       ],
39291       [
39292         "Zimbabwe",
39293         "zw",
39294         "263"
39295       ],
39296       [
39297         "Åland Islands",
39298         "ax",
39299         "358",
39300         1
39301       ]
39302   ];
39303   
39304   return d;
39305 }/**
39306 *    This script refer to:
39307 *    Title: International Telephone Input
39308 *    Author: Jack O'Connor
39309 *    Code version:  v12.1.12
39310 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39311 **/
39312
39313 /**
39314  * @class Roo.bootstrap.PhoneInput
39315  * @extends Roo.bootstrap.TriggerField
39316  * An input with International dial-code selection
39317  
39318  * @cfg {String} defaultDialCode default '+852'
39319  * @cfg {Array} preferedCountries default []
39320   
39321  * @constructor
39322  * Create a new PhoneInput.
39323  * @param {Object} config Configuration options
39324  */
39325
39326 Roo.bootstrap.PhoneInput = function(config) {
39327     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39328 };
39329
39330 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39331         
39332         listWidth: undefined,
39333         
39334         selectedClass: 'active',
39335         
39336         invalidClass : "has-warning",
39337         
39338         validClass: 'has-success',
39339         
39340         allowed: '0123456789',
39341         
39342         /**
39343          * @cfg {String} defaultDialCode The default dial code when initializing the input
39344          */
39345         defaultDialCode: '+852',
39346         
39347         /**
39348          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39349          */
39350         preferedCountries: false,
39351         
39352         getAutoCreate : function()
39353         {
39354             var data = Roo.bootstrap.PhoneInputData();
39355             var align = this.labelAlign || this.parentLabelAlign();
39356             var id = Roo.id();
39357             
39358             this.allCountries = [];
39359             this.dialCodeMapping = [];
39360             
39361             for (var i = 0; i < data.length; i++) {
39362               var c = data[i];
39363               this.allCountries[i] = {
39364                 name: c[0],
39365                 iso2: c[1],
39366                 dialCode: c[2],
39367                 priority: c[3] || 0,
39368                 areaCodes: c[4] || null
39369               };
39370               this.dialCodeMapping[c[2]] = {
39371                   name: c[0],
39372                   iso2: c[1],
39373                   priority: c[3] || 0,
39374                   areaCodes: c[4] || null
39375               };
39376             }
39377             
39378             var cfg = {
39379                 cls: 'form-group',
39380                 cn: []
39381             };
39382             
39383             var input =  {
39384                 tag: 'input',
39385                 id : id,
39386                 cls : 'form-control tel-input',
39387                 autocomplete: 'new-password'
39388             };
39389             
39390             var hiddenInput = {
39391                 tag: 'input',
39392                 type: 'hidden',
39393                 cls: 'hidden-tel-input'
39394             };
39395             
39396             if (this.name) {
39397                 hiddenInput.name = this.name;
39398             }
39399             
39400             if (this.disabled) {
39401                 input.disabled = true;
39402             }
39403             
39404             var flag_container = {
39405                 tag: 'div',
39406                 cls: 'flag-box',
39407                 cn: [
39408                     {
39409                         tag: 'div',
39410                         cls: 'flag'
39411                     },
39412                     {
39413                         tag: 'div',
39414                         cls: 'caret'
39415                     }
39416                 ]
39417             };
39418             
39419             var box = {
39420                 tag: 'div',
39421                 cls: this.hasFeedback ? 'has-feedback' : '',
39422                 cn: [
39423                     hiddenInput,
39424                     input,
39425                     {
39426                         tag: 'input',
39427                         cls: 'dial-code-holder',
39428                         disabled: true
39429                     }
39430                 ]
39431             };
39432             
39433             var container = {
39434                 cls: 'roo-select2-container input-group',
39435                 cn: [
39436                     flag_container,
39437                     box
39438                 ]
39439             };
39440             
39441             if (this.fieldLabel.length) {
39442                 var indicator = {
39443                     tag: 'i',
39444                     tooltip: 'This field is required'
39445                 };
39446                 
39447                 var label = {
39448                     tag: 'label',
39449                     'for':  id,
39450                     cls: 'control-label',
39451                     cn: []
39452                 };
39453                 
39454                 var label_text = {
39455                     tag: 'span',
39456                     html: this.fieldLabel
39457                 };
39458                 
39459                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39460                 label.cn = [
39461                     indicator,
39462                     label_text
39463                 ];
39464                 
39465                 if(this.indicatorpos == 'right') {
39466                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39467                     label.cn = [
39468                         label_text,
39469                         indicator
39470                     ];
39471                 }
39472                 
39473                 if(align == 'left') {
39474                     container = {
39475                         tag: 'div',
39476                         cn: [
39477                             container
39478                         ]
39479                     };
39480                     
39481                     if(this.labelWidth > 12){
39482                         label.style = "width: " + this.labelWidth + 'px';
39483                     }
39484                     if(this.labelWidth < 13 && this.labelmd == 0){
39485                         this.labelmd = this.labelWidth;
39486                     }
39487                     if(this.labellg > 0){
39488                         label.cls += ' col-lg-' + this.labellg;
39489                         input.cls += ' col-lg-' + (12 - this.labellg);
39490                     }
39491                     if(this.labelmd > 0){
39492                         label.cls += ' col-md-' + this.labelmd;
39493                         container.cls += ' col-md-' + (12 - this.labelmd);
39494                     }
39495                     if(this.labelsm > 0){
39496                         label.cls += ' col-sm-' + this.labelsm;
39497                         container.cls += ' col-sm-' + (12 - this.labelsm);
39498                     }
39499                     if(this.labelxs > 0){
39500                         label.cls += ' col-xs-' + this.labelxs;
39501                         container.cls += ' col-xs-' + (12 - this.labelxs);
39502                     }
39503                 }
39504             }
39505             
39506             cfg.cn = [
39507                 label,
39508                 container
39509             ];
39510             
39511             var settings = this;
39512             
39513             ['xs','sm','md','lg'].map(function(size){
39514                 if (settings[size]) {
39515                     cfg.cls += ' col-' + size + '-' + settings[size];
39516                 }
39517             });
39518             
39519             this.store = new Roo.data.Store({
39520                 proxy : new Roo.data.MemoryProxy({}),
39521                 reader : new Roo.data.JsonReader({
39522                     fields : [
39523                         {
39524                             'name' : 'name',
39525                             'type' : 'string'
39526                         },
39527                         {
39528                             'name' : 'iso2',
39529                             'type' : 'string'
39530                         },
39531                         {
39532                             'name' : 'dialCode',
39533                             'type' : 'string'
39534                         },
39535                         {
39536                             'name' : 'priority',
39537                             'type' : 'string'
39538                         },
39539                         {
39540                             'name' : 'areaCodes',
39541                             'type' : 'string'
39542                         }
39543                     ]
39544                 })
39545             });
39546             
39547             if(!this.preferedCountries) {
39548                 this.preferedCountries = [
39549                     'hk',
39550                     'gb',
39551                     'us'
39552                 ];
39553             }
39554             
39555             var p = this.preferedCountries.reverse();
39556             
39557             if(p) {
39558                 for (var i = 0; i < p.length; i++) {
39559                     for (var j = 0; j < this.allCountries.length; j++) {
39560                         if(this.allCountries[j].iso2 == p[i]) {
39561                             var t = this.allCountries[j];
39562                             this.allCountries.splice(j,1);
39563                             this.allCountries.unshift(t);
39564                         }
39565                     } 
39566                 }
39567             }
39568             
39569             this.store.proxy.data = {
39570                 success: true,
39571                 data: this.allCountries
39572             };
39573             
39574             return cfg;
39575         },
39576         
39577         initEvents : function()
39578         {
39579             this.createList();
39580             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39581             
39582             this.indicator = this.indicatorEl();
39583             this.flag = this.flagEl();
39584             this.dialCodeHolder = this.dialCodeHolderEl();
39585             
39586             this.trigger = this.el.select('div.flag-box',true).first();
39587             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39588             
39589             var _this = this;
39590             
39591             (function(){
39592                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39593                 _this.list.setWidth(lw);
39594             }).defer(100);
39595             
39596             this.list.on('mouseover', this.onViewOver, this);
39597             this.list.on('mousemove', this.onViewMove, this);
39598             this.inputEl().on("keyup", this.onKeyUp, this);
39599             
39600             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39601
39602             this.view = new Roo.View(this.list, this.tpl, {
39603                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39604             });
39605             
39606             this.view.on('click', this.onViewClick, this);
39607             this.setValue(this.defaultDialCode);
39608         },
39609         
39610         onTriggerClick : function(e)
39611         {
39612             Roo.log('trigger click');
39613             if(this.disabled){
39614                 return;
39615             }
39616             
39617             if(this.isExpanded()){
39618                 this.collapse();
39619                 this.hasFocus = false;
39620             }else {
39621                 this.store.load({});
39622                 this.hasFocus = true;
39623                 this.expand();
39624             }
39625         },
39626         
39627         isExpanded : function()
39628         {
39629             return this.list.isVisible();
39630         },
39631         
39632         collapse : function()
39633         {
39634             if(!this.isExpanded()){
39635                 return;
39636             }
39637             this.list.hide();
39638             Roo.get(document).un('mousedown', this.collapseIf, this);
39639             Roo.get(document).un('mousewheel', this.collapseIf, this);
39640             this.fireEvent('collapse', this);
39641             this.validate();
39642         },
39643         
39644         expand : function()
39645         {
39646             Roo.log('expand');
39647
39648             if(this.isExpanded() || !this.hasFocus){
39649                 return;
39650             }
39651             
39652             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39653             this.list.setWidth(lw);
39654             
39655             this.list.show();
39656             this.restrictHeight();
39657             
39658             Roo.get(document).on('mousedown', this.collapseIf, this);
39659             Roo.get(document).on('mousewheel', this.collapseIf, this);
39660             
39661             this.fireEvent('expand', this);
39662         },
39663         
39664         restrictHeight : function()
39665         {
39666             this.list.alignTo(this.inputEl(), this.listAlign);
39667             this.list.alignTo(this.inputEl(), this.listAlign);
39668         },
39669         
39670         onViewOver : function(e, t)
39671         {
39672             if(this.inKeyMode){
39673                 return;
39674             }
39675             var item = this.view.findItemFromChild(t);
39676             
39677             if(item){
39678                 var index = this.view.indexOf(item);
39679                 this.select(index, false);
39680             }
39681         },
39682
39683         // private
39684         onViewClick : function(view, doFocus, el, e)
39685         {
39686             var index = this.view.getSelectedIndexes()[0];
39687             
39688             var r = this.store.getAt(index);
39689             
39690             if(r){
39691                 this.onSelect(r, index);
39692             }
39693             if(doFocus !== false && !this.blockFocus){
39694                 this.inputEl().focus();
39695             }
39696         },
39697         
39698         onViewMove : function(e, t)
39699         {
39700             this.inKeyMode = false;
39701         },
39702         
39703         select : function(index, scrollIntoView)
39704         {
39705             this.selectedIndex = index;
39706             this.view.select(index);
39707             if(scrollIntoView !== false){
39708                 var el = this.view.getNode(index);
39709                 if(el){
39710                     this.list.scrollChildIntoView(el, false);
39711                 }
39712             }
39713         },
39714         
39715         createList : function()
39716         {
39717             this.list = Roo.get(document.body).createChild({
39718                 tag: 'ul',
39719                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39720                 style: 'display:none'
39721             });
39722             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39723         },
39724         
39725         collapseIf : function(e)
39726         {
39727             var in_combo  = e.within(this.el);
39728             var in_list =  e.within(this.list);
39729             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39730             
39731             if (in_combo || in_list || is_list) {
39732                 return;
39733             }
39734             this.collapse();
39735         },
39736         
39737         onSelect : function(record, index)
39738         {
39739             if(this.fireEvent('beforeselect', this, record, index) !== false){
39740                 
39741                 this.setFlagClass(record.data.iso2);
39742                 this.setDialCode(record.data.dialCode);
39743                 this.hasFocus = false;
39744                 this.collapse();
39745                 this.fireEvent('select', this, record, index);
39746             }
39747         },
39748         
39749         flagEl : function()
39750         {
39751             var flag = this.el.select('div.flag',true).first();
39752             if(!flag){
39753                 return false;
39754             }
39755             return flag;
39756         },
39757         
39758         dialCodeHolderEl : function()
39759         {
39760             var d = this.el.select('input.dial-code-holder',true).first();
39761             if(!d){
39762                 return false;
39763             }
39764             return d;
39765         },
39766         
39767         setDialCode : function(v)
39768         {
39769             this.dialCodeHolder.dom.value = '+'+v;
39770         },
39771         
39772         setFlagClass : function(n)
39773         {
39774             this.flag.dom.className = 'flag '+n;
39775         },
39776         
39777         getValue : function()
39778         {
39779             var v = this.inputEl().getValue();
39780             if(this.dialCodeHolder) {
39781                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39782             }
39783             return v;
39784         },
39785         
39786         setValue : function(v)
39787         {
39788             var d = this.getDialCode(v);
39789             
39790             //invalid dial code
39791             if(v.length == 0 || !d || d.length == 0) {
39792                 if(this.rendered){
39793                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39794                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39795                 }
39796                 return;
39797             }
39798             
39799             //valid dial code
39800             this.setFlagClass(this.dialCodeMapping[d].iso2);
39801             this.setDialCode(d);
39802             this.inputEl().dom.value = v.replace('+'+d,'');
39803             this.hiddenEl().dom.value = this.getValue();
39804             
39805             this.validate();
39806         },
39807         
39808         getDialCode : function(v = '')
39809         {
39810             if (v.length == 0) {
39811                 return this.dialCodeHolder.dom.value;
39812             }
39813             
39814             var dialCode = "";
39815             if (v.charAt(0) != "+") {
39816                 return false;
39817             }
39818             var numericChars = "";
39819             for (var i = 1; i < v.length; i++) {
39820               var c = v.charAt(i);
39821               if (!isNaN(c)) {
39822                 numericChars += c;
39823                 if (this.dialCodeMapping[numericChars]) {
39824                   dialCode = v.substr(1, i);
39825                 }
39826                 if (numericChars.length == 4) {
39827                   break;
39828                 }
39829               }
39830             }
39831             return dialCode;
39832         },
39833         
39834         reset : function()
39835         {
39836             this.setValue(this.defaultDialCode);
39837             this.validate();
39838         },
39839         
39840         hiddenEl : function()
39841         {
39842             return this.el.select('input.hidden-tel-input',true).first();
39843         },
39844         
39845         onKeyUp : function(e){
39846             
39847             var k = e.getKey();
39848             var c = e.getCharCode();
39849             
39850             if(
39851                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39852                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39853             ){
39854                 e.stopEvent();
39855             }
39856             
39857             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39858             //     return;
39859             // }
39860             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39861                 e.stopEvent();
39862             }
39863             
39864             this.setValue(this.getValue());
39865         }
39866         
39867 });
39868 /**
39869  * @class Roo.bootstrap.MoneyField
39870  * @extends Roo.bootstrap.ComboBox
39871  * Bootstrap MoneyField class
39872  * 
39873  * @constructor
39874  * Create a new MoneyField.
39875  * @param {Object} config Configuration options
39876  */
39877
39878 Roo.bootstrap.MoneyField = function(config) {
39879     
39880     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39881     
39882 };
39883
39884 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39885     
39886     /**
39887      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39888      */
39889     allowDecimals : true,
39890     /**
39891      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39892      */
39893     decimalSeparator : ".",
39894     /**
39895      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39896      */
39897     decimalPrecision : 2,
39898     /**
39899      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39900      */
39901     allowNegative : true,
39902     /**
39903      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39904      */
39905     minValue : Number.NEGATIVE_INFINITY,
39906     /**
39907      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39908      */
39909     maxValue : Number.MAX_VALUE,
39910     /**
39911      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39912      */
39913     minText : "The minimum value for this field is {0}",
39914     /**
39915      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39916      */
39917     maxText : "The maximum value for this field is {0}",
39918     /**
39919      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39920      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39921      */
39922     nanText : "{0} is not a valid number",
39923     /**
39924      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39925      */
39926     castInt : true,
39927     
39928     inputlg : 9,
39929     inputmd : 9,
39930     inputsm : 9,
39931     inputxs : 6,
39932     
39933     store : false,
39934     
39935     getAutoCreate : function()
39936     {
39937         var align = this.labelAlign || this.parentLabelAlign();
39938         
39939         var id = Roo.id();
39940
39941         var cfg = {
39942             cls: 'form-group',
39943             cn: []
39944         };
39945
39946         var input =  {
39947             tag: 'input',
39948             id : id,
39949             cls : 'form-control roo-money-amount-input',
39950             autocomplete: 'new-password'
39951         };
39952         
39953         if (this.name) {
39954             input.name = this.name;
39955         }
39956
39957         if (this.disabled) {
39958             input.disabled = true;
39959         }
39960
39961         var clg = 12 - this.inputlg;
39962         var cmd = 12 - this.inputmd;
39963         var csm = 12 - this.inputsm;
39964         var cxs = 12 - this.inputxs;
39965         
39966         var container = {
39967             tag : 'div',
39968             cls : 'row roo-money-field',
39969             cn : [
39970                 {
39971                     tag : 'div',
39972                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39973                     cn : [
39974                         {
39975                             tag : 'div',
39976                             cls: 'roo-select2-container input-group',
39977                             cn: [
39978                                 {
39979                                     tag : 'input',
39980                                     cls : 'form-control roo-money-currency-input',
39981                                     autocomplete: 'new-password',
39982                                     readOnly : 1,
39983                                     name : this.currencyName
39984                                 },
39985                                 {
39986                                     tag :'span',
39987                                     cls : 'input-group-addon',
39988                                     cn : [
39989                                         {
39990                                             tag: 'span',
39991                                             cls: 'caret'
39992                                         }
39993                                     ]
39994                                 }
39995                             ]
39996                         }
39997                     ]
39998                 },
39999                 {
40000                     tag : 'div',
40001                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40002                     cn : [
40003                         {
40004                             tag: 'div',
40005                             cls: this.hasFeedback ? 'has-feedback' : '',
40006                             cn: [
40007                                 input
40008                             ]
40009                         }
40010                     ]
40011                 }
40012             ]
40013             
40014         };
40015         
40016         if (this.fieldLabel.length) {
40017             var indicator = {
40018                 tag: 'i',
40019                 tooltip: 'This field is required'
40020             };
40021
40022             var label = {
40023                 tag: 'label',
40024                 'for':  id,
40025                 cls: 'control-label',
40026                 cn: []
40027             };
40028
40029             var label_text = {
40030                 tag: 'span',
40031                 html: this.fieldLabel
40032             };
40033
40034             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40035             label.cn = [
40036                 indicator,
40037                 label_text
40038             ];
40039
40040             if(this.indicatorpos == 'right') {
40041                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40042                 label.cn = [
40043                     label_text,
40044                     indicator
40045                 ];
40046             }
40047
40048             if(align == 'left') {
40049                 container = {
40050                     tag: 'div',
40051                     cn: [
40052                         container
40053                     ]
40054                 };
40055
40056                 if(this.labelWidth > 12){
40057                     label.style = "width: " + this.labelWidth + 'px';
40058                 }
40059                 if(this.labelWidth < 13 && this.labelmd == 0){
40060                     this.labelmd = this.labelWidth;
40061                 }
40062                 if(this.labellg > 0){
40063                     label.cls += ' col-lg-' + this.labellg;
40064                     input.cls += ' col-lg-' + (12 - this.labellg);
40065                 }
40066                 if(this.labelmd > 0){
40067                     label.cls += ' col-md-' + this.labelmd;
40068                     container.cls += ' col-md-' + (12 - this.labelmd);
40069                 }
40070                 if(this.labelsm > 0){
40071                     label.cls += ' col-sm-' + this.labelsm;
40072                     container.cls += ' col-sm-' + (12 - this.labelsm);
40073                 }
40074                 if(this.labelxs > 0){
40075                     label.cls += ' col-xs-' + this.labelxs;
40076                     container.cls += ' col-xs-' + (12 - this.labelxs);
40077                 }
40078             }
40079         }
40080
40081         cfg.cn = [
40082             label,
40083             container
40084         ];
40085
40086         var settings = this;
40087
40088         ['xs','sm','md','lg'].map(function(size){
40089             if (settings[size]) {
40090                 cfg.cls += ' col-' + size + '-' + settings[size];
40091             }
40092         });
40093         
40094         return cfg;
40095         
40096     },
40097     
40098     initEvents : function()
40099     {
40100         this.indicator = this.indicatorEl();
40101         
40102         this.initCurrencyEvent();
40103         
40104         this.initNumberEvent();
40105         
40106     },
40107     
40108     initCurrencyEvent : function()
40109     {
40110         if (!this.store) {
40111             throw "can not find store for combo";
40112         }
40113         
40114         this.store = Roo.factory(this.store, Roo.data);
40115         this.store.parent = this;
40116         
40117         this.createList();
40118         
40119         this.triggerEl = this.el.select('.input-group-addon', true).first();
40120         
40121         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40122         
40123         var _this = this;
40124         
40125         (function(){
40126             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40127             _this.list.setWidth(lw);
40128         }).defer(100);
40129         
40130         this.list.on('mouseover', this.onViewOver, this);
40131         this.list.on('mousemove', this.onViewMove, this);
40132         this.list.on('scroll', this.onViewScroll, this);
40133         
40134         if(!this.tpl){
40135             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40136         }
40137         
40138         this.view = new Roo.View(this.list, this.tpl, {
40139             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40140         });
40141         
40142         this.view.on('click', this.onViewClick, this);
40143         
40144         this.store.on('beforeload', this.onBeforeLoad, this);
40145         this.store.on('load', this.onLoad, this);
40146         this.store.on('loadexception', this.onLoadException, this);
40147         
40148         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40149             "up" : function(e){
40150                 this.inKeyMode = true;
40151                 this.selectPrev();
40152             },
40153
40154             "down" : function(e){
40155                 if(!this.isExpanded()){
40156                     this.onTriggerClick();
40157                 }else{
40158                     this.inKeyMode = true;
40159                     this.selectNext();
40160                 }
40161             },
40162
40163             "enter" : function(e){
40164                 this.collapse();
40165                 
40166                 if(this.fireEvent("specialkey", this, e)){
40167                     this.onViewClick(false);
40168                 }
40169                 
40170                 return true;
40171             },
40172
40173             "esc" : function(e){
40174                 this.collapse();
40175             },
40176
40177             "tab" : function(e){
40178                 this.collapse();
40179                 
40180                 if(this.fireEvent("specialkey", this, e)){
40181                     this.onViewClick(false);
40182                 }
40183                 
40184                 return true;
40185             },
40186
40187             scope : this,
40188
40189             doRelay : function(foo, bar, hname){
40190                 if(hname == 'down' || this.scope.isExpanded()){
40191                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40192                 }
40193                 return true;
40194             },
40195
40196             forceKeyDown: true
40197         });
40198         
40199         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40200         
40201     },
40202     
40203     initNumberEvent : function(e)
40204     {
40205         this.inputEl().on("keydown" , this.fireKey,  this);
40206         this.inputEl().on("focus", this.onFocus,  this);
40207         this.inputEl().on("blur", this.onBlur,  this);
40208         
40209         this.inputEl().relayEvent('keyup', this);
40210         
40211         if(this.indicator){
40212             this.indicator.addClass('invisible');
40213         }
40214  
40215         this.originalValue = this.getValue();
40216         
40217         if(this.validationEvent == 'keyup'){
40218             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40219             this.inputEl().on('keyup', this.filterValidation, this);
40220         }
40221         else if(this.validationEvent !== false){
40222             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40223         }
40224         
40225         if(this.selectOnFocus){
40226             this.on("focus", this.preFocus, this);
40227             
40228         }
40229         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40230             this.inputEl().on("keypress", this.filterKeys, this);
40231         } else {
40232             this.inputEl().relayEvent('keypress', this);
40233         }
40234         
40235         var allowed = "0123456789";
40236         
40237         if(this.allowDecimals){
40238             allowed += this.decimalSeparator;
40239         }
40240         
40241         if(this.allowNegative){
40242             allowed += "-";
40243         }
40244         
40245         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40246         
40247         var keyPress = function(e){
40248             
40249             var k = e.getKey();
40250             
40251             var c = e.getCharCode();
40252             
40253             if(
40254                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40255                     allowed.indexOf(String.fromCharCode(c)) === -1
40256             ){
40257                 e.stopEvent();
40258                 return;
40259             }
40260             
40261             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40262                 return;
40263             }
40264             
40265             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40266                 e.stopEvent();
40267             }
40268         };
40269         
40270         this.inputEl().on("keypress", keyPress, this);
40271         
40272     },
40273     
40274     onTriggerClick : function(e)
40275     {   
40276         if(this.disabled){
40277             return;
40278         }
40279         
40280         this.page = 0;
40281         this.loadNext = false;
40282         
40283         if(this.isExpanded()){
40284             this.collapse();
40285             return;
40286         }
40287         
40288         this.hasFocus = true;
40289         
40290         if(this.triggerAction == 'all') {
40291             this.doQuery(this.allQuery, true);
40292             return;
40293         }
40294         
40295         this.doQuery(this.getRawValue());
40296     },
40297     
40298     getCurrency : function()
40299     {   
40300         var v = this.currencyEl().getValue();
40301         
40302         return v;
40303     },
40304     
40305     restrictHeight : function()
40306     {
40307         this.list.alignTo(this.currencyEl(), this.listAlign);
40308         this.list.alignTo(this.currencyEl(), this.listAlign);
40309     },
40310     
40311     onViewClick : function(view, doFocus, el, e)
40312     {
40313         var index = this.view.getSelectedIndexes()[0];
40314         
40315         var r = this.store.getAt(index);
40316         
40317         if(r){
40318             this.onSelect(r, index);
40319         }
40320     },
40321     
40322     onSelect : function(record, index){
40323         
40324         if(this.fireEvent('beforeselect', this, record, index) !== false){
40325         
40326             this.setFromCurrencyData(index > -1 ? record.data : false);
40327             
40328             this.collapse();
40329             
40330             this.fireEvent('select', this, record, index);
40331         }
40332     },
40333     
40334     setFromCurrencyData : function(o)
40335     {
40336         var currency = '';
40337         
40338         this.lastCurrency = o;
40339         
40340         if (this.currencyField) {
40341             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40342         } else {
40343             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40344         }
40345         
40346         this.lastSelectionText = currency;
40347         
40348         this.setCurrency(currency);
40349     },
40350     
40351     setFromData : function(o)
40352     {
40353         var c = {};
40354         
40355         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40356         
40357         this.setFromCurrencyData(c);
40358         
40359         var value = '';
40360         
40361         if (this.name) {
40362             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40363         } else {
40364             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40365         }
40366         
40367         this.setValue(value);
40368         
40369     },
40370     
40371     setCurrency : function(v)
40372     {   
40373         this.currencyValue = v;
40374         
40375         if(this.rendered){
40376             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40377             this.validate();
40378         }
40379     },
40380     
40381     setValue : function(v)
40382     {
40383         v = this.fixPrecision(v);
40384         
40385         v = String(v).replace(".", this.decimalSeparator);
40386         
40387         this.value = v;
40388         
40389         if(this.rendered){
40390             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40391             this.validate();
40392         }
40393     },
40394     
40395     getRawValue : function()
40396     {
40397         var v = this.inputEl().getValue();
40398         
40399         return v;
40400     },
40401     
40402     getValue : function()
40403     {
40404         return this.fixPrecision(this.parseValue(this.getRawValue()));
40405     },
40406     
40407     parseValue : function(value)
40408     {
40409         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40410         return isNaN(value) ? '' : value;
40411     },
40412     
40413     fixPrecision : function(value)
40414     {
40415         var nan = isNaN(value);
40416         
40417         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40418             return nan ? '' : value;
40419         }
40420         
40421         return parseFloat(value).toFixed(this.decimalPrecision);
40422     },
40423     
40424     decimalPrecisionFcn : function(v)
40425     {
40426         return Math.floor(v);
40427     },
40428     
40429     validateValue : function(value)
40430     {
40431         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40432             return false;
40433         }
40434         
40435         var num = this.parseValue(value);
40436         
40437         if(isNaN(num)){
40438             this.markInvalid(String.format(this.nanText, value));
40439             return false;
40440         }
40441         
40442         if(num < this.minValue){
40443             this.markInvalid(String.format(this.minText, this.minValue));
40444             return false;
40445         }
40446         
40447         if(num > this.maxValue){
40448             this.markInvalid(String.format(this.maxText, this.maxValue));
40449             return false;
40450         }
40451         
40452         return true;
40453     },
40454     
40455     validate : function()
40456     {
40457         if(this.disabled || this.allowBlank){
40458             this.markValid();
40459             return true;
40460         }
40461         
40462         var currency = this.getCurrency();
40463         
40464         if(this.validateValue(this.getRawValue()) && currency.length){
40465             this.markValid();
40466             return true;
40467         }
40468         
40469         this.markInvalid();
40470         return false;
40471     },
40472     
40473     getName: function()
40474     {
40475         return this.name;
40476     },
40477     
40478     beforeBlur : function()
40479     {
40480         if(!this.castInt){
40481             return;
40482         }
40483         
40484         var v = this.parseValue(this.getRawValue());
40485         
40486         if(v){
40487             this.setValue(v);
40488         }
40489     },
40490     
40491     onBlur : function()
40492     {
40493         this.beforeBlur();
40494         
40495         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40496             //this.el.removeClass(this.focusClass);
40497         }
40498         
40499         this.hasFocus = false;
40500         
40501         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40502             this.validate();
40503         }
40504         
40505         var v = this.getValue();
40506         
40507         if(String(v) !== String(this.startValue)){
40508             this.fireEvent('change', this, v, this.startValue);
40509         }
40510         
40511         this.fireEvent("blur", this);
40512     },
40513     
40514     inputEl : function()
40515     {
40516         return this.el.select('.roo-money-amount-input', true).first();
40517     },
40518     
40519     currencyEl : function()
40520     {
40521         return this.el.select('.roo-money-currency-input', true).first();
40522     }
40523     
40524 });