Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
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 (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.getHeight() + (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     getItems : function()
8042     {
8043         var r=new Roo.util.MixedCollection(false, function(o){
8044             return o.id || (o.id = Roo.id());
8045         });
8046         var iter = function(el) {
8047             if (el.inputEl) {
8048                 r.add(el);
8049             }
8050             if (!el.items) {
8051                 return;
8052             }
8053             Roo.each(el.items,function(e) {
8054                 iter(e);
8055             });
8056
8057
8058         };
8059
8060         iter(this);
8061         return r;
8062
8063
8064
8065
8066     }
8067
8068 });
8069
8070 Roo.apply(Roo.bootstrap.Form, {
8071     
8072     popover : {
8073         
8074         padding : 5,
8075         
8076         isApplied : false,
8077         
8078         isMasked : false,
8079         
8080         form : false,
8081         
8082         target : false,
8083         
8084         toolTip : false,
8085         
8086         intervalID : false,
8087         
8088         maskEl : false,
8089         
8090         apply : function()
8091         {
8092             if(this.isApplied){
8093                 return;
8094             }
8095             
8096             this.maskEl = {
8097                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8098                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8099                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8100                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8101             };
8102             
8103             this.maskEl.top.enableDisplayMode("block");
8104             this.maskEl.left.enableDisplayMode("block");
8105             this.maskEl.bottom.enableDisplayMode("block");
8106             this.maskEl.right.enableDisplayMode("block");
8107             
8108             this.toolTip = new Roo.bootstrap.Tooltip({
8109                 cls : 'roo-form-error-popover',
8110                 alignment : {
8111                     'left' : ['r-l', [-2,0], 'right'],
8112                     'right' : ['l-r', [2,0], 'left'],
8113                     'bottom' : ['tl-bl', [0,2], 'top'],
8114                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8115                 }
8116             });
8117             
8118             this.toolTip.render(Roo.get(document.body));
8119
8120             this.toolTip.el.enableDisplayMode("block");
8121             
8122             Roo.get(document.body).on('click', function(){
8123                 this.unmask();
8124             }, this);
8125             
8126             Roo.get(document.body).on('touchstart', function(){
8127                 this.unmask();
8128             }, this);
8129             
8130             this.isApplied = true
8131         },
8132         
8133         mask : function(form, target)
8134         {
8135             this.form = form;
8136             
8137             this.target = target;
8138             
8139             if(!this.form.errorMask || !target.el){
8140                 return;
8141             }
8142             
8143             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8144             
8145             Roo.log(scrollable);
8146             
8147             var ot = this.target.el.calcOffsetsTo(scrollable);
8148             
8149             var scrollTo = ot[1] - this.form.maskOffset;
8150             
8151             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8152             
8153             scrollable.scrollTo('top', scrollTo);
8154             
8155             var box = this.target.el.getBox();
8156             Roo.log(box);
8157             var zIndex = Roo.bootstrap.Modal.zIndex++;
8158
8159             
8160             this.maskEl.top.setStyle('position', 'absolute');
8161             this.maskEl.top.setStyle('z-index', zIndex);
8162             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8163             this.maskEl.top.setLeft(0);
8164             this.maskEl.top.setTop(0);
8165             this.maskEl.top.show();
8166             
8167             this.maskEl.left.setStyle('position', 'absolute');
8168             this.maskEl.left.setStyle('z-index', zIndex);
8169             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8170             this.maskEl.left.setLeft(0);
8171             this.maskEl.left.setTop(box.y - this.padding);
8172             this.maskEl.left.show();
8173
8174             this.maskEl.bottom.setStyle('position', 'absolute');
8175             this.maskEl.bottom.setStyle('z-index', zIndex);
8176             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8177             this.maskEl.bottom.setLeft(0);
8178             this.maskEl.bottom.setTop(box.bottom + this.padding);
8179             this.maskEl.bottom.show();
8180
8181             this.maskEl.right.setStyle('position', 'absolute');
8182             this.maskEl.right.setStyle('z-index', zIndex);
8183             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8184             this.maskEl.right.setLeft(box.right + this.padding);
8185             this.maskEl.right.setTop(box.y - this.padding);
8186             this.maskEl.right.show();
8187
8188             this.toolTip.bindEl = this.target.el;
8189
8190             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8191
8192             var tip = this.target.blankText;
8193
8194             if(this.target.getValue() !== '' ) {
8195                 
8196                 if (this.target.invalidText.length) {
8197                     tip = this.target.invalidText;
8198                 } else if (this.target.regexText.length){
8199                     tip = this.target.regexText;
8200                 }
8201             }
8202
8203             this.toolTip.show(tip);
8204
8205             this.intervalID = window.setInterval(function() {
8206                 Roo.bootstrap.Form.popover.unmask();
8207             }, 10000);
8208
8209             window.onwheel = function(){ return false;};
8210             
8211             (function(){ this.isMasked = true; }).defer(500, this);
8212             
8213         },
8214         
8215         unmask : function()
8216         {
8217             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8218                 return;
8219             }
8220             
8221             this.maskEl.top.setStyle('position', 'absolute');
8222             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8223             this.maskEl.top.hide();
8224
8225             this.maskEl.left.setStyle('position', 'absolute');
8226             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8227             this.maskEl.left.hide();
8228
8229             this.maskEl.bottom.setStyle('position', 'absolute');
8230             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8231             this.maskEl.bottom.hide();
8232
8233             this.maskEl.right.setStyle('position', 'absolute');
8234             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8235             this.maskEl.right.hide();
8236             
8237             this.toolTip.hide();
8238             
8239             this.toolTip.el.hide();
8240             
8241             window.onwheel = function(){ return true;};
8242             
8243             if(this.intervalID){
8244                 window.clearInterval(this.intervalID);
8245                 this.intervalID = false;
8246             }
8247             
8248             this.isMasked = false;
8249             
8250         }
8251         
8252     }
8253     
8254 });
8255
8256 /*
8257  * Based on:
8258  * Ext JS Library 1.1.1
8259  * Copyright(c) 2006-2007, Ext JS, LLC.
8260  *
8261  * Originally Released Under LGPL - original licence link has changed is not relivant.
8262  *
8263  * Fork - LGPL
8264  * <script type="text/javascript">
8265  */
8266 /**
8267  * @class Roo.form.VTypes
8268  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8269  * @singleton
8270  */
8271 Roo.form.VTypes = function(){
8272     // closure these in so they are only created once.
8273     var alpha = /^[a-zA-Z_]+$/;
8274     var alphanum = /^[a-zA-Z0-9_]+$/;
8275     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8276     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8277
8278     // All these messages and functions are configurable
8279     return {
8280         /**
8281          * The function used to validate email addresses
8282          * @param {String} value The email address
8283          */
8284         'email' : function(v){
8285             return email.test(v);
8286         },
8287         /**
8288          * The error text to display when the email validation function returns false
8289          * @type String
8290          */
8291         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8292         /**
8293          * The keystroke filter mask to be applied on email input
8294          * @type RegExp
8295          */
8296         'emailMask' : /[a-z0-9_\.\-@]/i,
8297
8298         /**
8299          * The function used to validate URLs
8300          * @param {String} value The URL
8301          */
8302         'url' : function(v){
8303             return url.test(v);
8304         },
8305         /**
8306          * The error text to display when the url validation function returns false
8307          * @type String
8308          */
8309         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8310         
8311         /**
8312          * The function used to validate alpha values
8313          * @param {String} value The value
8314          */
8315         'alpha' : function(v){
8316             return alpha.test(v);
8317         },
8318         /**
8319          * The error text to display when the alpha validation function returns false
8320          * @type String
8321          */
8322         'alphaText' : 'This field should only contain letters and _',
8323         /**
8324          * The keystroke filter mask to be applied on alpha input
8325          * @type RegExp
8326          */
8327         'alphaMask' : /[a-z_]/i,
8328
8329         /**
8330          * The function used to validate alphanumeric values
8331          * @param {String} value The value
8332          */
8333         'alphanum' : function(v){
8334             return alphanum.test(v);
8335         },
8336         /**
8337          * The error text to display when the alphanumeric validation function returns false
8338          * @type String
8339          */
8340         'alphanumText' : 'This field should only contain letters, numbers and _',
8341         /**
8342          * The keystroke filter mask to be applied on alphanumeric input
8343          * @type RegExp
8344          */
8345         'alphanumMask' : /[a-z0-9_]/i
8346     };
8347 }();/*
8348  * - LGPL
8349  *
8350  * Input
8351  * 
8352  */
8353
8354 /**
8355  * @class Roo.bootstrap.Input
8356  * @extends Roo.bootstrap.Component
8357  * Bootstrap Input class
8358  * @cfg {Boolean} disabled is it disabled
8359  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8360  * @cfg {String} name name of the input
8361  * @cfg {string} fieldLabel - the label associated
8362  * @cfg {string} placeholder - placeholder to put in text.
8363  * @cfg {string}  before - input group add on before
8364  * @cfg {string} after - input group add on after
8365  * @cfg {string} size - (lg|sm) or leave empty..
8366  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8367  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8368  * @cfg {Number} md colspan out of 12 for computer-sized screens
8369  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8370  * @cfg {string} value default value of the input
8371  * @cfg {Number} labelWidth set the width of label 
8372  * @cfg {Number} labellg set the width of label (1-12)
8373  * @cfg {Number} labelmd set the width of label (1-12)
8374  * @cfg {Number} labelsm set the width of label (1-12)
8375  * @cfg {Number} labelxs set the width of label (1-12)
8376  * @cfg {String} labelAlign (top|left)
8377  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8378  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8379  * @cfg {String} indicatorpos (left|right) default left
8380
8381  * @cfg {String} align (left|center|right) Default left
8382  * @cfg {Boolean} forceFeedback (true|false) Default false
8383  * 
8384  * 
8385  * 
8386  * 
8387  * @constructor
8388  * Create a new Input
8389  * @param {Object} config The config object
8390  */
8391
8392 Roo.bootstrap.Input = function(config){
8393     
8394     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8395     
8396     this.addEvents({
8397         /**
8398          * @event focus
8399          * Fires when this field receives input focus.
8400          * @param {Roo.form.Field} this
8401          */
8402         focus : true,
8403         /**
8404          * @event blur
8405          * Fires when this field loses input focus.
8406          * @param {Roo.form.Field} this
8407          */
8408         blur : true,
8409         /**
8410          * @event specialkey
8411          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8412          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8413          * @param {Roo.form.Field} this
8414          * @param {Roo.EventObject} e The event object
8415          */
8416         specialkey : true,
8417         /**
8418          * @event change
8419          * Fires just before the field blurs if the field value has changed.
8420          * @param {Roo.form.Field} this
8421          * @param {Mixed} newValue The new value
8422          * @param {Mixed} oldValue The original value
8423          */
8424         change : true,
8425         /**
8426          * @event invalid
8427          * Fires after the field has been marked as invalid.
8428          * @param {Roo.form.Field} this
8429          * @param {String} msg The validation message
8430          */
8431         invalid : true,
8432         /**
8433          * @event valid
8434          * Fires after the field has been validated with no errors.
8435          * @param {Roo.form.Field} this
8436          */
8437         valid : true,
8438          /**
8439          * @event keyup
8440          * Fires after the key up
8441          * @param {Roo.form.Field} this
8442          * @param {Roo.EventObject}  e The event Object
8443          */
8444         keyup : true
8445     });
8446 };
8447
8448 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8449      /**
8450      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8451       automatic validation (defaults to "keyup").
8452      */
8453     validationEvent : "keyup",
8454      /**
8455      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8456      */
8457     validateOnBlur : true,
8458     /**
8459      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8460      */
8461     validationDelay : 250,
8462      /**
8463      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8464      */
8465     focusClass : "x-form-focus",  // not needed???
8466     
8467        
8468     /**
8469      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8470      */
8471     invalidClass : "has-warning",
8472     
8473     /**
8474      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8475      */
8476     validClass : "has-success",
8477     
8478     /**
8479      * @cfg {Boolean} hasFeedback (true|false) default true
8480      */
8481     hasFeedback : true,
8482     
8483     /**
8484      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8485      */
8486     invalidFeedbackClass : "glyphicon-warning-sign",
8487     
8488     /**
8489      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8490      */
8491     validFeedbackClass : "glyphicon-ok",
8492     
8493     /**
8494      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8495      */
8496     selectOnFocus : false,
8497     
8498      /**
8499      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8500      */
8501     maskRe : null,
8502        /**
8503      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8504      */
8505     vtype : null,
8506     
8507       /**
8508      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8509      */
8510     disableKeyFilter : false,
8511     
8512        /**
8513      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8514      */
8515     disabled : false,
8516      /**
8517      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8518      */
8519     allowBlank : true,
8520     /**
8521      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8522      */
8523     blankText : "Please complete this mandatory field",
8524     
8525      /**
8526      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8527      */
8528     minLength : 0,
8529     /**
8530      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8531      */
8532     maxLength : Number.MAX_VALUE,
8533     /**
8534      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8535      */
8536     minLengthText : "The minimum length for this field is {0}",
8537     /**
8538      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8539      */
8540     maxLengthText : "The maximum length for this field is {0}",
8541   
8542     
8543     /**
8544      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8545      * If available, this function will be called only after the basic validators all return true, and will be passed the
8546      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8547      */
8548     validator : null,
8549     /**
8550      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8551      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8552      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8553      */
8554     regex : null,
8555     /**
8556      * @cfg {String} regexText -- Depricated - use Invalid Text
8557      */
8558     regexText : "",
8559     
8560     /**
8561      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8562      */
8563     invalidText : "",
8564     
8565     
8566     
8567     autocomplete: false,
8568     
8569     
8570     fieldLabel : '',
8571     inputType : 'text',
8572     
8573     name : false,
8574     placeholder: false,
8575     before : false,
8576     after : false,
8577     size : false,
8578     hasFocus : false,
8579     preventMark: false,
8580     isFormField : true,
8581     value : '',
8582     labelWidth : 2,
8583     labelAlign : false,
8584     readOnly : false,
8585     align : false,
8586     formatedValue : false,
8587     forceFeedback : false,
8588     
8589     indicatorpos : 'left',
8590     
8591     labellg : 0,
8592     labelmd : 0,
8593     labelsm : 0,
8594     labelxs : 0,
8595     
8596     parentLabelAlign : function()
8597     {
8598         var parent = this;
8599         while (parent.parent()) {
8600             parent = parent.parent();
8601             if (typeof(parent.labelAlign) !='undefined') {
8602                 return parent.labelAlign;
8603             }
8604         }
8605         return 'left';
8606         
8607     },
8608     
8609     getAutoCreate : function()
8610     {
8611         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8612         
8613         var id = Roo.id();
8614         
8615         var cfg = {};
8616         
8617         if(this.inputType != 'hidden'){
8618             cfg.cls = 'form-group' //input-group
8619         }
8620         
8621         var input =  {
8622             tag: 'input',
8623             id : id,
8624             type : this.inputType,
8625             value : this.value,
8626             cls : 'form-control',
8627             placeholder : this.placeholder || '',
8628             autocomplete : this.autocomplete || 'new-password'
8629         };
8630         
8631         if(this.align){
8632             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8633         }
8634         
8635         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8636             input.maxLength = this.maxLength;
8637         }
8638         
8639         if (this.disabled) {
8640             input.disabled=true;
8641         }
8642         
8643         if (this.readOnly) {
8644             input.readonly=true;
8645         }
8646         
8647         if (this.name) {
8648             input.name = this.name;
8649         }
8650         
8651         if (this.size) {
8652             input.cls += ' input-' + this.size;
8653         }
8654         
8655         var settings=this;
8656         ['xs','sm','md','lg'].map(function(size){
8657             if (settings[size]) {
8658                 cfg.cls += ' col-' + size + '-' + settings[size];
8659             }
8660         });
8661         
8662         var inputblock = input;
8663         
8664         var feedback = {
8665             tag: 'span',
8666             cls: 'glyphicon form-control-feedback'
8667         };
8668             
8669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8670             
8671             inputblock = {
8672                 cls : 'has-feedback',
8673                 cn :  [
8674                     input,
8675                     feedback
8676                 ] 
8677             };  
8678         }
8679         
8680         if (this.before || this.after) {
8681             
8682             inputblock = {
8683                 cls : 'input-group',
8684                 cn :  [] 
8685             };
8686             
8687             if (this.before && typeof(this.before) == 'string') {
8688                 
8689                 inputblock.cn.push({
8690                     tag :'span',
8691                     cls : 'roo-input-before input-group-addon',
8692                     html : this.before
8693                 });
8694             }
8695             if (this.before && typeof(this.before) == 'object') {
8696                 this.before = Roo.factory(this.before);
8697                 
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-before input-group-' +
8701                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8702                 });
8703             }
8704             
8705             inputblock.cn.push(input);
8706             
8707             if (this.after && typeof(this.after) == 'string') {
8708                 inputblock.cn.push({
8709                     tag :'span',
8710                     cls : 'roo-input-after input-group-addon',
8711                     html : this.after
8712                 });
8713             }
8714             if (this.after && typeof(this.after) == 'object') {
8715                 this.after = Roo.factory(this.after);
8716                 
8717                 inputblock.cn.push({
8718                     tag :'span',
8719                     cls : 'roo-input-after input-group-' +
8720                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8721                 });
8722             }
8723             
8724             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8725                 inputblock.cls += ' has-feedback';
8726                 inputblock.cn.push(feedback);
8727             }
8728         };
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732             cfg.cls += ' roo-form-group-label-left';
8733             
8734             cfg.cn = [
8735                 {
8736                     tag : 'i',
8737                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8738                     tooltip : 'This field is required'
8739                 },
8740                 {
8741                     tag: 'label',
8742                     'for' :  id,
8743                     cls : 'control-label',
8744                     html : this.fieldLabel
8745
8746                 },
8747                 {
8748                     cls : "", 
8749                     cn: [
8750                         inputblock
8751                     ]
8752                 }
8753             ];
8754             
8755             var labelCfg = cfg.cn[1];
8756             var contentCfg = cfg.cn[2];
8757             
8758             if(this.indicatorpos == 'right'){
8759                 cfg.cn = [
8760                     {
8761                         tag: 'label',
8762                         'for' :  id,
8763                         cls : 'control-label',
8764                         cn : [
8765                             {
8766                                 tag : 'span',
8767                                 html : this.fieldLabel
8768                             },
8769                             {
8770                                 tag : 'i',
8771                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8772                                 tooltip : 'This field is required'
8773                             }
8774                         ]
8775                     },
8776                     {
8777                         cls : "",
8778                         cn: [
8779                             inputblock
8780                         ]
8781                     }
8782
8783                 ];
8784                 
8785                 labelCfg = cfg.cn[0];
8786                 contentCfg = cfg.cn[1];
8787             
8788             }
8789             
8790             if(this.labelWidth > 12){
8791                 labelCfg.style = "width: " + this.labelWidth + 'px';
8792             }
8793             
8794             if(this.labelWidth < 13 && this.labelmd == 0){
8795                 this.labelmd = this.labelWidth;
8796             }
8797             
8798             if(this.labellg > 0){
8799                 labelCfg.cls += ' col-lg-' + this.labellg;
8800                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8801             }
8802             
8803             if(this.labelmd > 0){
8804                 labelCfg.cls += ' col-md-' + this.labelmd;
8805                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8806             }
8807             
8808             if(this.labelsm > 0){
8809                 labelCfg.cls += ' col-sm-' + this.labelsm;
8810                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8811             }
8812             
8813             if(this.labelxs > 0){
8814                 labelCfg.cls += ' col-xs-' + this.labelxs;
8815                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8816             }
8817             
8818             
8819         } else if ( this.fieldLabel.length) {
8820                 
8821             cfg.cn = [
8822                 {
8823                     tag : 'i',
8824                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8825                     tooltip : 'This field is required'
8826                 },
8827                 {
8828                     tag: 'label',
8829                    //cls : 'input-group-addon',
8830                     html : this.fieldLabel
8831
8832                 },
8833
8834                inputblock
8835
8836            ];
8837            
8838            if(this.indicatorpos == 'right'){
8839                 
8840                 cfg.cn = [
8841                     {
8842                         tag: 'label',
8843                        //cls : 'input-group-addon',
8844                         html : this.fieldLabel
8845
8846                     },
8847                     {
8848                         tag : 'i',
8849                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8850                         tooltip : 'This field is required'
8851                     },
8852
8853                    inputblock
8854
8855                ];
8856
8857             }
8858
8859         } else {
8860             
8861             cfg.cn = [
8862
8863                     inputblock
8864
8865             ];
8866                 
8867                 
8868         };
8869         
8870         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8871            cfg.cls += ' navbar-form';
8872         }
8873         
8874         if (this.parentType === 'NavGroup') {
8875            cfg.cls += ' navbar-form';
8876            cfg.tag = 'li';
8877         }
8878         
8879         return cfg;
8880         
8881     },
8882     /**
8883      * return the real input element.
8884      */
8885     inputEl: function ()
8886     {
8887         return this.el.select('input.form-control',true).first();
8888     },
8889     
8890     tooltipEl : function()
8891     {
8892         return this.inputEl();
8893     },
8894     
8895     indicatorEl : function()
8896     {
8897         var indicator = this.el.select('i.roo-required-indicator',true).first();
8898         
8899         if(!indicator){
8900             return false;
8901         }
8902         
8903         return indicator;
8904         
8905     },
8906     
8907     setDisabled : function(v)
8908     {
8909         var i  = this.inputEl().dom;
8910         if (!v) {
8911             i.removeAttribute('disabled');
8912             return;
8913             
8914         }
8915         i.setAttribute('disabled','true');
8916     },
8917     initEvents : function()
8918     {
8919           
8920         this.inputEl().on("keydown" , this.fireKey,  this);
8921         this.inputEl().on("focus", this.onFocus,  this);
8922         this.inputEl().on("blur", this.onBlur,  this);
8923         
8924         this.inputEl().relayEvent('keyup', this);
8925         
8926         this.indicator = this.indicatorEl();
8927         
8928         if(this.indicator){
8929             this.indicator.addClass('invisible');
8930             
8931         }
8932  
8933         // reference to original value for reset
8934         this.originalValue = this.getValue();
8935         //Roo.form.TextField.superclass.initEvents.call(this);
8936         if(this.validationEvent == 'keyup'){
8937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8938             this.inputEl().on('keyup', this.filterValidation, this);
8939         }
8940         else if(this.validationEvent !== false){
8941             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8942         }
8943         
8944         if(this.selectOnFocus){
8945             this.on("focus", this.preFocus, this);
8946             
8947         }
8948         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8949             this.inputEl().on("keypress", this.filterKeys, this);
8950         } else {
8951             this.inputEl().relayEvent('keypress', this);
8952         }
8953        /* if(this.grow){
8954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8955             this.el.on("click", this.autoSize,  this);
8956         }
8957         */
8958         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8959             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8960         }
8961         
8962         if (typeof(this.before) == 'object') {
8963             this.before.render(this.el.select('.roo-input-before',true).first());
8964         }
8965         if (typeof(this.after) == 'object') {
8966             this.after.render(this.el.select('.roo-input-after',true).first());
8967         }
8968         
8969         
8970     },
8971     filterValidation : function(e){
8972         if(!e.isNavKeyPress()){
8973             this.validationTask.delay(this.validationDelay);
8974         }
8975     },
8976      /**
8977      * Validates the field value
8978      * @return {Boolean} True if the value is valid, else false
8979      */
8980     validate : function(){
8981         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8982         if(this.disabled || this.validateValue(this.getRawValue())){
8983             this.markValid();
8984             return true;
8985         }
8986         
8987         this.markInvalid();
8988         return false;
8989     },
8990     
8991     
8992     /**
8993      * Validates a value according to the field's validation rules and marks the field as invalid
8994      * if the validation fails
8995      * @param {Mixed} value The value to validate
8996      * @return {Boolean} True if the value is valid, else false
8997      */
8998     validateValue : function(value){
8999         if(value.length < 1)  { // if it's blank
9000             if(this.allowBlank){
9001                 return true;
9002             }            
9003             return this.inputEl().hasClass('hide') ? true : false;
9004         }
9005         
9006         if(value.length < this.minLength){
9007             return false;
9008         }
9009         if(value.length > this.maxLength){
9010             return false;
9011         }
9012         if(this.vtype){
9013             var vt = Roo.form.VTypes;
9014             if(!vt[this.vtype](value, this)){
9015                 return false;
9016             }
9017         }
9018         if(typeof this.validator == "function"){
9019             var msg = this.validator(value);
9020             if(msg !== true){
9021                 return false;
9022             }
9023             if (typeof(msg) == 'string') {
9024                 this.invalidText = msg;
9025             }
9026         }
9027         
9028         if(this.regex && !this.regex.test(value)){
9029             return false;
9030         }
9031         
9032         return true;
9033     },
9034
9035     
9036     
9037      // private
9038     fireKey : function(e){
9039         //Roo.log('field ' + e.getKey());
9040         if(e.isNavKeyPress()){
9041             this.fireEvent("specialkey", this, e);
9042         }
9043     },
9044     focus : function (selectText){
9045         if(this.rendered){
9046             this.inputEl().focus();
9047             if(selectText === true){
9048                 this.inputEl().dom.select();
9049             }
9050         }
9051         return this;
9052     } ,
9053     
9054     onFocus : function(){
9055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9056            // this.el.addClass(this.focusClass);
9057         }
9058         if(!this.hasFocus){
9059             this.hasFocus = true;
9060             this.startValue = this.getValue();
9061             this.fireEvent("focus", this);
9062         }
9063     },
9064     
9065     beforeBlur : Roo.emptyFn,
9066
9067     
9068     // private
9069     onBlur : function(){
9070         this.beforeBlur();
9071         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9072             //this.el.removeClass(this.focusClass);
9073         }
9074         this.hasFocus = false;
9075         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9076             this.validate();
9077         }
9078         var v = this.getValue();
9079         if(String(v) !== String(this.startValue)){
9080             this.fireEvent('change', this, v, this.startValue);
9081         }
9082         this.fireEvent("blur", this);
9083     },
9084     
9085     /**
9086      * Resets the current field value to the originally loaded value and clears any validation messages
9087      */
9088     reset : function(){
9089         this.setValue(this.originalValue);
9090         this.validate();
9091     },
9092      /**
9093      * Returns the name of the field
9094      * @return {Mixed} name The name field
9095      */
9096     getName: function(){
9097         return this.name;
9098     },
9099      /**
9100      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getValue : function(){
9104         
9105         var v = this.inputEl().getValue();
9106         
9107         return v;
9108     },
9109     /**
9110      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9111      * @return {Mixed} value The field value
9112      */
9113     getRawValue : function(){
9114         var v = this.inputEl().getValue();
9115         
9116         return v;
9117     },
9118     
9119     /**
9120      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9121      * @param {Mixed} value The value to set
9122      */
9123     setRawValue : function(v){
9124         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9125     },
9126     
9127     selectText : function(start, end){
9128         var v = this.getRawValue();
9129         if(v.length > 0){
9130             start = start === undefined ? 0 : start;
9131             end = end === undefined ? v.length : end;
9132             var d = this.inputEl().dom;
9133             if(d.setSelectionRange){
9134                 d.setSelectionRange(start, end);
9135             }else if(d.createTextRange){
9136                 var range = d.createTextRange();
9137                 range.moveStart("character", start);
9138                 range.moveEnd("character", v.length-end);
9139                 range.select();
9140             }
9141         }
9142     },
9143     
9144     /**
9145      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9146      * @param {Mixed} value The value to set
9147      */
9148     setValue : function(v){
9149         this.value = v;
9150         if(this.rendered){
9151             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9152             this.validate();
9153         }
9154     },
9155     
9156     /*
9157     processValue : function(value){
9158         if(this.stripCharsRe){
9159             var newValue = value.replace(this.stripCharsRe, '');
9160             if(newValue !== value){
9161                 this.setRawValue(newValue);
9162                 return newValue;
9163             }
9164         }
9165         return value;
9166     },
9167   */
9168     preFocus : function(){
9169         
9170         if(this.selectOnFocus){
9171             this.inputEl().dom.select();
9172         }
9173     },
9174     filterKeys : function(e){
9175         var k = e.getKey();
9176         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9177             return;
9178         }
9179         var c = e.getCharCode(), cc = String.fromCharCode(c);
9180         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9181             return;
9182         }
9183         if(!this.maskRe.test(cc)){
9184             e.stopEvent();
9185         }
9186     },
9187      /**
9188      * Clear any invalid styles/messages for this field
9189      */
9190     clearInvalid : function(){
9191         
9192         if(!this.el || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196      
9197         this.el.removeClass(this.invalidClass);
9198         
9199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9200             
9201             var feedback = this.el.select('.form-control-feedback', true).first();
9202             
9203             if(feedback){
9204                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9205             }
9206             
9207         }
9208         
9209         this.fireEvent('valid', this);
9210     },
9211     
9212      /**
9213      * Mark this field as valid
9214      */
9215     markValid : function()
9216     {
9217         if(!this.el  || this.preventMark){ // not rendered...
9218             return;
9219         }
9220         
9221         this.el.removeClass([this.invalidClass, this.validClass]);
9222         
9223         var feedback = this.el.select('.form-control-feedback', true).first();
9224             
9225         if(feedback){
9226             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9227         }
9228
9229         if(this.disabled){
9230             return;
9231         }
9232         
9233         if(this.allowBlank && !this.getRawValue().length){
9234             return;
9235         }
9236         
9237         if(this.indicator){
9238             this.indicator.removeClass('visible');
9239             this.indicator.addClass('invisible');
9240         }
9241         
9242         this.el.addClass(this.validClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9251             }
9252             
9253         }
9254         
9255         this.fireEvent('valid', this);
9256     },
9257     
9258      /**
9259      * Mark this field as invalid
9260      * @param {String} msg The validation message
9261      */
9262     markInvalid : function(msg)
9263     {
9264         if(!this.el  || this.preventMark){ // not rendered
9265             return;
9266         }
9267         
9268         this.el.removeClass([this.invalidClass, this.validClass]);
9269         
9270         var feedback = this.el.select('.form-control-feedback', true).first();
9271             
9272         if(feedback){
9273             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274         }
9275
9276         if(this.disabled){
9277             return;
9278         }
9279         
9280         if(this.allowBlank && !this.getRawValue().length){
9281             return;
9282         }
9283         
9284         if(this.indicator){
9285             this.indicator.removeClass('invisible');
9286             this.indicator.addClass('visible');
9287         }
9288         
9289         this.el.addClass(this.invalidClass);
9290         
9291         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9292             
9293             var feedback = this.el.select('.form-control-feedback', true).first();
9294             
9295             if(feedback){
9296                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9297                 
9298                 if(this.getValue().length || this.forceFeedback){
9299                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9300                 }
9301                 
9302             }
9303             
9304         }
9305         
9306         this.fireEvent('invalid', this, msg);
9307     },
9308     // private
9309     SafariOnKeyDown : function(event)
9310     {
9311         // this is a workaround for a password hang bug on chrome/ webkit.
9312         if (this.inputEl().dom.type != 'password') {
9313             return;
9314         }
9315         
9316         var isSelectAll = false;
9317         
9318         if(this.inputEl().dom.selectionEnd > 0){
9319             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9320         }
9321         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9322             event.preventDefault();
9323             this.setValue('');
9324             return;
9325         }
9326         
9327         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9328             
9329             event.preventDefault();
9330             // this is very hacky as keydown always get's upper case.
9331             //
9332             var cc = String.fromCharCode(event.getCharCode());
9333             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9334             
9335         }
9336     },
9337     adjustWidth : function(tag, w){
9338         tag = tag.toLowerCase();
9339         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9340             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9341                 if(tag == 'input'){
9342                     return w + 2;
9343                 }
9344                 if(tag == 'textarea'){
9345                     return w-2;
9346                 }
9347             }else if(Roo.isOpera){
9348                 if(tag == 'input'){
9349                     return w + 2;
9350                 }
9351                 if(tag == 'textarea'){
9352                     return w-2;
9353                 }
9354             }
9355         }
9356         return w;
9357     },
9358     
9359     setFieldLabel : function(v)
9360     {
9361         this.fieldLabel = v;
9362         
9363         if(this.rendered){
9364             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9365         }
9366     }
9367 });
9368
9369  
9370 /*
9371  * - LGPL
9372  *
9373  * Input
9374  * 
9375  */
9376
9377 /**
9378  * @class Roo.bootstrap.TextArea
9379  * @extends Roo.bootstrap.Input
9380  * Bootstrap TextArea class
9381  * @cfg {Number} cols Specifies the visible width of a text area
9382  * @cfg {Number} rows Specifies the visible number of lines in a text area
9383  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9384  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9385  * @cfg {string} html text
9386  * 
9387  * @constructor
9388  * Create a new TextArea
9389  * @param {Object} config The config object
9390  */
9391
9392 Roo.bootstrap.TextArea = function(config){
9393     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9394    
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9398      
9399     cols : false,
9400     rows : 5,
9401     readOnly : false,
9402     warp : 'soft',
9403     resize : false,
9404     value: false,
9405     html: false,
9406     
9407     getAutoCreate : function(){
9408         
9409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9410         
9411         var id = Roo.id();
9412         
9413         var cfg = {};
9414         
9415         if(this.inputType != 'hidden'){
9416             cfg.cls = 'form-group' //input-group
9417         }
9418         
9419         var input =  {
9420             tag: 'textarea',
9421             id : id,
9422             warp : this.warp,
9423             rows : this.rows,
9424             value : this.value || '',
9425             html: this.html || '',
9426             cls : 'form-control',
9427             placeholder : this.placeholder || '' 
9428             
9429         };
9430         
9431         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9432             input.maxLength = this.maxLength;
9433         }
9434         
9435         if(this.resize){
9436             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9437         }
9438         
9439         if(this.cols){
9440             input.cols = this.cols;
9441         }
9442         
9443         if (this.readOnly) {
9444             input.readonly = true;
9445         }
9446         
9447         if (this.name) {
9448             input.name = this.name;
9449         }
9450         
9451         if (this.size) {
9452             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9453         }
9454         
9455         var settings=this;
9456         ['xs','sm','md','lg'].map(function(size){
9457             if (settings[size]) {
9458                 cfg.cls += ' col-' + size + '-' + settings[size];
9459             }
9460         });
9461         
9462         var inputblock = input;
9463         
9464         if(this.hasFeedback && !this.allowBlank){
9465             
9466             var feedback = {
9467                 tag: 'span',
9468                 cls: 'glyphicon form-control-feedback'
9469             };
9470
9471             inputblock = {
9472                 cls : 'has-feedback',
9473                 cn :  [
9474                     input,
9475                     feedback
9476                 ] 
9477             };  
9478         }
9479         
9480         
9481         if (this.before || this.after) {
9482             
9483             inputblock = {
9484                 cls : 'input-group',
9485                 cn :  [] 
9486             };
9487             if (this.before) {
9488                 inputblock.cn.push({
9489                     tag :'span',
9490                     cls : 'input-group-addon',
9491                     html : this.before
9492                 });
9493             }
9494             
9495             inputblock.cn.push(input);
9496             
9497             if(this.hasFeedback && !this.allowBlank){
9498                 inputblock.cls += ' has-feedback';
9499                 inputblock.cn.push(feedback);
9500             }
9501             
9502             if (this.after) {
9503                 inputblock.cn.push({
9504                     tag :'span',
9505                     cls : 'input-group-addon',
9506                     html : this.after
9507                 });
9508             }
9509             
9510         }
9511         
9512         if (align ==='left' && this.fieldLabel.length) {
9513             cfg.cn = [
9514                 {
9515                     tag: 'label',
9516                     'for' :  id,
9517                     cls : 'control-label',
9518                     html : this.fieldLabel
9519                 },
9520                 {
9521                     cls : "",
9522                     cn: [
9523                         inputblock
9524                     ]
9525                 }
9526
9527             ];
9528             
9529             if(this.labelWidth > 12){
9530                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9531             }
9532
9533             if(this.labelWidth < 13 && this.labelmd == 0){
9534                 this.labelmd = this.labelWidth;
9535             }
9536
9537             if(this.labellg > 0){
9538                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9539                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9540             }
9541
9542             if(this.labelmd > 0){
9543                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9544                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9545             }
9546
9547             if(this.labelsm > 0){
9548                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9549                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9550             }
9551
9552             if(this.labelxs > 0){
9553                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9554                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9555             }
9556             
9557         } else if ( this.fieldLabel.length) {
9558             cfg.cn = [
9559
9560                {
9561                    tag: 'label',
9562                    //cls : 'input-group-addon',
9563                    html : this.fieldLabel
9564
9565                },
9566
9567                inputblock
9568
9569            ];
9570
9571         } else {
9572
9573             cfg.cn = [
9574
9575                 inputblock
9576
9577             ];
9578                 
9579         }
9580         
9581         if (this.disabled) {
9582             input.disabled=true;
9583         }
9584         
9585         return cfg;
9586         
9587     },
9588     /**
9589      * return the real textarea element.
9590      */
9591     inputEl: function ()
9592     {
9593         return this.el.select('textarea.form-control',true).first();
9594     },
9595     
9596     /**
9597      * Clear any invalid styles/messages for this field
9598      */
9599     clearInvalid : function()
9600     {
9601         
9602         if(!this.el || this.preventMark){ // not rendered
9603             return;
9604         }
9605         
9606         var label = this.el.select('label', true).first();
9607         var icon = this.el.select('i.fa-star', true).first();
9608         
9609         if(label && icon){
9610             icon.remove();
9611         }
9612         
9613         this.el.removeClass(this.invalidClass);
9614         
9615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9616             
9617             var feedback = this.el.select('.form-control-feedback', true).first();
9618             
9619             if(feedback){
9620                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9621             }
9622             
9623         }
9624         
9625         this.fireEvent('valid', this);
9626     },
9627     
9628      /**
9629      * Mark this field as valid
9630      */
9631     markValid : function()
9632     {
9633         if(!this.el  || this.preventMark){ // not rendered
9634             return;
9635         }
9636         
9637         this.el.removeClass([this.invalidClass, this.validClass]);
9638         
9639         var feedback = this.el.select('.form-control-feedback', true).first();
9640             
9641         if(feedback){
9642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9643         }
9644
9645         if(this.disabled || this.allowBlank){
9646             return;
9647         }
9648         
9649         var label = this.el.select('label', true).first();
9650         var icon = this.el.select('i.fa-star', true).first();
9651         
9652         if(label && icon){
9653             icon.remove();
9654         }
9655         
9656         this.el.addClass(this.validClass);
9657         
9658         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9659             
9660             var feedback = this.el.select('.form-control-feedback', true).first();
9661             
9662             if(feedback){
9663                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9664                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9665             }
9666             
9667         }
9668         
9669         this.fireEvent('valid', this);
9670     },
9671     
9672      /**
9673      * Mark this field as invalid
9674      * @param {String} msg The validation message
9675      */
9676     markInvalid : function(msg)
9677     {
9678         if(!this.el  || this.preventMark){ // not rendered
9679             return;
9680         }
9681         
9682         this.el.removeClass([this.invalidClass, this.validClass]);
9683         
9684         var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686         if(feedback){
9687             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9688         }
9689
9690         if(this.disabled || this.allowBlank){
9691             return;
9692         }
9693         
9694         var label = this.el.select('label', true).first();
9695         var icon = this.el.select('i.fa-star', true).first();
9696         
9697         if(!this.getValue().length && label && !icon){
9698             this.el.createChild({
9699                 tag : 'i',
9700                 cls : 'text-danger fa fa-lg fa-star',
9701                 tooltip : 'This field is required',
9702                 style : 'margin-right:5px;'
9703             }, label, true);
9704         }
9705
9706         this.el.addClass(this.invalidClass);
9707         
9708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709             
9710             var feedback = this.el.select('.form-control-feedback', true).first();
9711             
9712             if(feedback){
9713                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9714                 
9715                 if(this.getValue().length || this.forceFeedback){
9716                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9717                 }
9718                 
9719             }
9720             
9721         }
9722         
9723         this.fireEvent('invalid', this, msg);
9724     }
9725 });
9726
9727  
9728 /*
9729  * - LGPL
9730  *
9731  * trigger field - base class for combo..
9732  * 
9733  */
9734  
9735 /**
9736  * @class Roo.bootstrap.TriggerField
9737  * @extends Roo.bootstrap.Input
9738  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9739  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9740  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9741  * for which you can provide a custom implementation.  For example:
9742  * <pre><code>
9743 var trigger = new Roo.bootstrap.TriggerField();
9744 trigger.onTriggerClick = myTriggerFn;
9745 trigger.applyTo('my-field');
9746 </code></pre>
9747  *
9748  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9749  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9750  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9751  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9752  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9753
9754  * @constructor
9755  * Create a new TriggerField.
9756  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9757  * to the base TextField)
9758  */
9759 Roo.bootstrap.TriggerField = function(config){
9760     this.mimicing = false;
9761     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9762 };
9763
9764 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9765     /**
9766      * @cfg {String} triggerClass A CSS class to apply to the trigger
9767      */
9768      /**
9769      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9770      */
9771     hideTrigger:false,
9772
9773     /**
9774      * @cfg {Boolean} removable (true|false) special filter default false
9775      */
9776     removable : false,
9777     
9778     /** @cfg {Boolean} grow @hide */
9779     /** @cfg {Number} growMin @hide */
9780     /** @cfg {Number} growMax @hide */
9781
9782     /**
9783      * @hide 
9784      * @method
9785      */
9786     autoSize: Roo.emptyFn,
9787     // private
9788     monitorTab : true,
9789     // private
9790     deferHeight : true,
9791
9792     
9793     actionMode : 'wrap',
9794     
9795     caret : false,
9796     
9797     
9798     getAutoCreate : function(){
9799        
9800         var align = this.labelAlign || this.parentLabelAlign();
9801         
9802         var id = Roo.id();
9803         
9804         var cfg = {
9805             cls: 'form-group' //input-group
9806         };
9807         
9808         
9809         var input =  {
9810             tag: 'input',
9811             id : id,
9812             type : this.inputType,
9813             cls : 'form-control',
9814             autocomplete: 'new-password',
9815             placeholder : this.placeholder || '' 
9816             
9817         };
9818         if (this.name) {
9819             input.name = this.name;
9820         }
9821         if (this.size) {
9822             input.cls += ' input-' + this.size;
9823         }
9824         
9825         if (this.disabled) {
9826             input.disabled=true;
9827         }
9828         
9829         var inputblock = input;
9830         
9831         if(this.hasFeedback && !this.allowBlank){
9832             
9833             var feedback = {
9834                 tag: 'span',
9835                 cls: 'glyphicon form-control-feedback'
9836             };
9837             
9838             if(this.removable && !this.editable && !this.tickable){
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         {
9844                             tag: 'button',
9845                             html : 'x',
9846                             cls : 'roo-combo-removable-btn close'
9847                         },
9848                         feedback
9849                     ] 
9850                 };
9851             } else {
9852                 inputblock = {
9853                     cls : 'has-feedback',
9854                     cn :  [
9855                         inputblock,
9856                         feedback
9857                     ] 
9858                 };
9859             }
9860
9861         } else {
9862             if(this.removable && !this.editable && !this.tickable){
9863                 inputblock = {
9864                     cls : 'roo-removable',
9865                     cn :  [
9866                         inputblock,
9867                         {
9868                             tag: 'button',
9869                             html : 'x',
9870                             cls : 'roo-combo-removable-btn close'
9871                         }
9872                     ] 
9873                 };
9874             }
9875         }
9876         
9877         if (this.before || this.after) {
9878             
9879             inputblock = {
9880                 cls : 'input-group',
9881                 cn :  [] 
9882             };
9883             if (this.before) {
9884                 inputblock.cn.push({
9885                     tag :'span',
9886                     cls : 'input-group-addon',
9887                     html : this.before
9888                 });
9889             }
9890             
9891             inputblock.cn.push(input);
9892             
9893             if(this.hasFeedback && !this.allowBlank){
9894                 inputblock.cls += ' has-feedback';
9895                 inputblock.cn.push(feedback);
9896             }
9897             
9898             if (this.after) {
9899                 inputblock.cn.push({
9900                     tag :'span',
9901                     cls : 'input-group-addon',
9902                     html : this.after
9903                 });
9904             }
9905             
9906         };
9907         
9908         var box = {
9909             tag: 'div',
9910             cn: [
9911                 {
9912                     tag: 'input',
9913                     type : 'hidden',
9914                     cls: 'form-hidden-field'
9915                 },
9916                 inputblock
9917             ]
9918             
9919         };
9920         
9921         if(this.multiple){
9922             box = {
9923                 tag: 'div',
9924                 cn: [
9925                     {
9926                         tag: 'input',
9927                         type : 'hidden',
9928                         cls: 'form-hidden-field'
9929                     },
9930                     {
9931                         tag: 'ul',
9932                         cls: 'roo-select2-choices',
9933                         cn:[
9934                             {
9935                                 tag: 'li',
9936                                 cls: 'roo-select2-search-field',
9937                                 cn: [
9938
9939                                     inputblock
9940                                 ]
9941                             }
9942                         ]
9943                     }
9944                 ]
9945             }
9946         };
9947         
9948         var combobox = {
9949             cls: 'roo-select2-container input-group',
9950             cn: [
9951                 box
9952 //                {
9953 //                    tag: 'ul',
9954 //                    cls: 'typeahead typeahead-long dropdown-menu',
9955 //                    style: 'display:none'
9956 //                }
9957             ]
9958         };
9959         
9960         if(!this.multiple && this.showToggleBtn){
9961             
9962             var caret = {
9963                         tag: 'span',
9964                         cls: 'caret'
9965              };
9966             if (this.caret != false) {
9967                 caret = {
9968                      tag: 'i',
9969                      cls: 'fa fa-' + this.caret
9970                 };
9971                 
9972             }
9973             
9974             combobox.cn.push({
9975                 tag :'span',
9976                 cls : 'input-group-addon btn dropdown-toggle',
9977                 cn : [
9978                     caret,
9979                     {
9980                         tag: 'span',
9981                         cls: 'combobox-clear',
9982                         cn  : [
9983                             {
9984                                 tag : 'i',
9985                                 cls: 'icon-remove'
9986                             }
9987                         ]
9988                     }
9989                 ]
9990
9991             })
9992         }
9993         
9994         if(this.multiple){
9995             combobox.cls += ' roo-select2-container-multi';
9996         }
9997         
9998         if (align ==='left' && this.fieldLabel.length) {
9999             
10000             cfg.cls += ' roo-form-group-label-left';
10001
10002             cfg.cn = [
10003                 {
10004                     tag : 'i',
10005                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10006                     tooltip : 'This field is required'
10007                 },
10008                 {
10009                     tag: 'label',
10010                     'for' :  id,
10011                     cls : 'control-label',
10012                     html : this.fieldLabel
10013
10014                 },
10015                 {
10016                     cls : "", 
10017                     cn: [
10018                         combobox
10019                     ]
10020                 }
10021
10022             ];
10023             
10024             var labelCfg = cfg.cn[1];
10025             var contentCfg = cfg.cn[2];
10026             
10027             if(this.indicatorpos == 'right'){
10028                 cfg.cn = [
10029                     {
10030                         tag: 'label',
10031                         'for' :  id,
10032                         cls : 'control-label',
10033                         cn : [
10034                             {
10035                                 tag : 'span',
10036                                 html : this.fieldLabel
10037                             },
10038                             {
10039                                 tag : 'i',
10040                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10041                                 tooltip : 'This field is required'
10042                             }
10043                         ]
10044                     },
10045                     {
10046                         cls : "", 
10047                         cn: [
10048                             combobox
10049                         ]
10050                     }
10051
10052                 ];
10053                 
10054                 labelCfg = cfg.cn[0];
10055                 contentCfg = cfg.cn[1];
10056             }
10057             
10058             if(this.labelWidth > 12){
10059                 labelCfg.style = "width: " + this.labelWidth + 'px';
10060             }
10061             
10062             if(this.labelWidth < 13 && this.labelmd == 0){
10063                 this.labelmd = this.labelWidth;
10064             }
10065             
10066             if(this.labellg > 0){
10067                 labelCfg.cls += ' col-lg-' + this.labellg;
10068                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10069             }
10070             
10071             if(this.labelmd > 0){
10072                 labelCfg.cls += ' col-md-' + this.labelmd;
10073                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10074             }
10075             
10076             if(this.labelsm > 0){
10077                 labelCfg.cls += ' col-sm-' + this.labelsm;
10078                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10079             }
10080             
10081             if(this.labelxs > 0){
10082                 labelCfg.cls += ' col-xs-' + this.labelxs;
10083                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10084             }
10085             
10086         } else if ( this.fieldLabel.length) {
10087 //                Roo.log(" label");
10088             cfg.cn = [
10089                 {
10090                    tag : 'i',
10091                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10092                    tooltip : 'This field is required'
10093                },
10094                {
10095                    tag: 'label',
10096                    //cls : 'input-group-addon',
10097                    html : this.fieldLabel
10098
10099                },
10100
10101                combobox
10102
10103             ];
10104             
10105             if(this.indicatorpos == 'right'){
10106                 
10107                 cfg.cn = [
10108                     {
10109                        tag: 'label',
10110                        cn : [
10111                            {
10112                                tag : 'span',
10113                                html : this.fieldLabel
10114                            },
10115                            {
10116                               tag : 'i',
10117                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10118                               tooltip : 'This field is required'
10119                            }
10120                        ]
10121
10122                     },
10123                     combobox
10124
10125                 ];
10126
10127             }
10128
10129         } else {
10130             
10131 //                Roo.log(" no label && no align");
10132                 cfg = combobox
10133                      
10134                 
10135         }
10136         
10137         var settings=this;
10138         ['xs','sm','md','lg'].map(function(size){
10139             if (settings[size]) {
10140                 cfg.cls += ' col-' + size + '-' + settings[size];
10141             }
10142         });
10143         
10144         return cfg;
10145         
10146     },
10147     
10148     
10149     
10150     // private
10151     onResize : function(w, h){
10152 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10153 //        if(typeof w == 'number'){
10154 //            var x = w - this.trigger.getWidth();
10155 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10156 //            this.trigger.setStyle('left', x+'px');
10157 //        }
10158     },
10159
10160     // private
10161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10162
10163     // private
10164     getResizeEl : function(){
10165         return this.inputEl();
10166     },
10167
10168     // private
10169     getPositionEl : function(){
10170         return this.inputEl();
10171     },
10172
10173     // private
10174     alignErrorIcon : function(){
10175         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10176     },
10177
10178     // private
10179     initEvents : function(){
10180         
10181         this.createList();
10182         
10183         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10184         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10185         if(!this.multiple && this.showToggleBtn){
10186             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10187             if(this.hideTrigger){
10188                 this.trigger.setDisplayed(false);
10189             }
10190             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10191         }
10192         
10193         if(this.multiple){
10194             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10195         }
10196         
10197         if(this.removable && !this.editable && !this.tickable){
10198             var close = this.closeTriggerEl();
10199             
10200             if(close){
10201                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10202                 close.on('click', this.removeBtnClick, this, close);
10203             }
10204         }
10205         
10206         //this.trigger.addClassOnOver('x-form-trigger-over');
10207         //this.trigger.addClassOnClick('x-form-trigger-click');
10208         
10209         //if(!this.width){
10210         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10211         //}
10212     },
10213     
10214     closeTriggerEl : function()
10215     {
10216         var close = this.el.select('.roo-combo-removable-btn', true).first();
10217         return close ? close : false;
10218     },
10219     
10220     removeBtnClick : function(e, h, el)
10221     {
10222         e.preventDefault();
10223         
10224         if(this.fireEvent("remove", this) !== false){
10225             this.reset();
10226             this.fireEvent("afterremove", this)
10227         }
10228     },
10229     
10230     createList : function()
10231     {
10232         this.list = Roo.get(document.body).createChild({
10233             tag: 'ul',
10234             cls: 'typeahead typeahead-long dropdown-menu',
10235             style: 'display:none'
10236         });
10237         
10238         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10239         
10240     },
10241
10242     // private
10243     initTrigger : function(){
10244        
10245     },
10246
10247     // private
10248     onDestroy : function(){
10249         if(this.trigger){
10250             this.trigger.removeAllListeners();
10251           //  this.trigger.remove();
10252         }
10253         //if(this.wrap){
10254         //    this.wrap.remove();
10255         //}
10256         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10257     },
10258
10259     // private
10260     onFocus : function(){
10261         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10262         /*
10263         if(!this.mimicing){
10264             this.wrap.addClass('x-trigger-wrap-focus');
10265             this.mimicing = true;
10266             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10267             if(this.monitorTab){
10268                 this.el.on("keydown", this.checkTab, this);
10269             }
10270         }
10271         */
10272     },
10273
10274     // private
10275     checkTab : function(e){
10276         if(e.getKey() == e.TAB){
10277             this.triggerBlur();
10278         }
10279     },
10280
10281     // private
10282     onBlur : function(){
10283         // do nothing
10284     },
10285
10286     // private
10287     mimicBlur : function(e, t){
10288         /*
10289         if(!this.wrap.contains(t) && this.validateBlur()){
10290             this.triggerBlur();
10291         }
10292         */
10293     },
10294
10295     // private
10296     triggerBlur : function(){
10297         this.mimicing = false;
10298         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10299         if(this.monitorTab){
10300             this.el.un("keydown", this.checkTab, this);
10301         }
10302         //this.wrap.removeClass('x-trigger-wrap-focus');
10303         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10304     },
10305
10306     // private
10307     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10308     validateBlur : function(e, t){
10309         return true;
10310     },
10311
10312     // private
10313     onDisable : function(){
10314         this.inputEl().dom.disabled = true;
10315         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10316         //if(this.wrap){
10317         //    this.wrap.addClass('x-item-disabled');
10318         //}
10319     },
10320
10321     // private
10322     onEnable : function(){
10323         this.inputEl().dom.disabled = false;
10324         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10325         //if(this.wrap){
10326         //    this.el.removeClass('x-item-disabled');
10327         //}
10328     },
10329
10330     // private
10331     onShow : function(){
10332         var ae = this.getActionEl();
10333         
10334         if(ae){
10335             ae.dom.style.display = '';
10336             ae.dom.style.visibility = 'visible';
10337         }
10338     },
10339
10340     // private
10341     
10342     onHide : function(){
10343         var ae = this.getActionEl();
10344         ae.dom.style.display = 'none';
10345     },
10346
10347     /**
10348      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10349      * by an implementing function.
10350      * @method
10351      * @param {EventObject} e
10352      */
10353     onTriggerClick : Roo.emptyFn
10354 });
10355  /*
10356  * Based on:
10357  * Ext JS Library 1.1.1
10358  * Copyright(c) 2006-2007, Ext JS, LLC.
10359  *
10360  * Originally Released Under LGPL - original licence link has changed is not relivant.
10361  *
10362  * Fork - LGPL
10363  * <script type="text/javascript">
10364  */
10365
10366
10367 /**
10368  * @class Roo.data.SortTypes
10369  * @singleton
10370  * Defines the default sorting (casting?) comparison functions used when sorting data.
10371  */
10372 Roo.data.SortTypes = {
10373     /**
10374      * Default sort that does nothing
10375      * @param {Mixed} s The value being converted
10376      * @return {Mixed} The comparison value
10377      */
10378     none : function(s){
10379         return s;
10380     },
10381     
10382     /**
10383      * The regular expression used to strip tags
10384      * @type {RegExp}
10385      * @property
10386      */
10387     stripTagsRE : /<\/?[^>]+>/gi,
10388     
10389     /**
10390      * Strips all HTML tags to sort on text only
10391      * @param {Mixed} s The value being converted
10392      * @return {String} The comparison value
10393      */
10394     asText : function(s){
10395         return String(s).replace(this.stripTagsRE, "");
10396     },
10397     
10398     /**
10399      * Strips all HTML tags to sort on text only - Case insensitive
10400      * @param {Mixed} s The value being converted
10401      * @return {String} The comparison value
10402      */
10403     asUCText : function(s){
10404         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10405     },
10406     
10407     /**
10408      * Case insensitive string
10409      * @param {Mixed} s The value being converted
10410      * @return {String} The comparison value
10411      */
10412     asUCString : function(s) {
10413         return String(s).toUpperCase();
10414     },
10415     
10416     /**
10417      * Date sorting
10418      * @param {Mixed} s The value being converted
10419      * @return {Number} The comparison value
10420      */
10421     asDate : function(s) {
10422         if(!s){
10423             return 0;
10424         }
10425         if(s instanceof Date){
10426             return s.getTime();
10427         }
10428         return Date.parse(String(s));
10429     },
10430     
10431     /**
10432      * Float sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Float} The comparison value
10435      */
10436     asFloat : function(s) {
10437         var val = parseFloat(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     },
10443     
10444     /**
10445      * Integer sorting
10446      * @param {Mixed} s The value being converted
10447      * @return {Number} The comparison value
10448      */
10449     asInt : function(s) {
10450         var val = parseInt(String(s).replace(/,/g, ""));
10451         if(isNaN(val)) {
10452             val = 0;
10453         }
10454         return val;
10455     }
10456 };/*
10457  * Based on:
10458  * Ext JS Library 1.1.1
10459  * Copyright(c) 2006-2007, Ext JS, LLC.
10460  *
10461  * Originally Released Under LGPL - original licence link has changed is not relivant.
10462  *
10463  * Fork - LGPL
10464  * <script type="text/javascript">
10465  */
10466
10467 /**
10468 * @class Roo.data.Record
10469  * Instances of this class encapsulate both record <em>definition</em> information, and record
10470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10471  * to access Records cached in an {@link Roo.data.Store} object.<br>
10472  * <p>
10473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10475  * objects.<br>
10476  * <p>
10477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10478  * @constructor
10479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10480  * {@link #create}. The parameters are the same.
10481  * @param {Array} data An associative Array of data values keyed by the field name.
10482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10484  * not specified an integer id is generated.
10485  */
10486 Roo.data.Record = function(data, id){
10487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10488     this.data = data;
10489 };
10490
10491 /**
10492  * Generate a constructor for a specific record layout.
10493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10495  * Each field definition object may contain the following properties: <ul>
10496  * <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,
10497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10503  * this may be omitted.</p></li>
10504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10505  * <ul><li>auto (Default, implies no conversion)</li>
10506  * <li>string</li>
10507  * <li>int</li>
10508  * <li>float</li>
10509  * <li>boolean</li>
10510  * <li>date</li></ul></p></li>
10511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10514  * by the Reader into an object that will be stored in the Record. It is passed the
10515  * following parameters:<ul>
10516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10517  * </ul></p></li>
10518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10519  * </ul>
10520  * <br>usage:<br><pre><code>
10521 var TopicRecord = Roo.data.Record.create(
10522     {name: 'title', mapping: 'topic_title'},
10523     {name: 'author', mapping: 'username'},
10524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10526     {name: 'lastPoster', mapping: 'user2'},
10527     {name: 'excerpt', mapping: 'post_text'}
10528 );
10529
10530 var myNewRecord = new TopicRecord({
10531     title: 'Do my job please',
10532     author: 'noobie',
10533     totalPosts: 1,
10534     lastPost: new Date(),
10535     lastPoster: 'Animal',
10536     excerpt: 'No way dude!'
10537 });
10538 myStore.add(myNewRecord);
10539 </code></pre>
10540  * @method create
10541  * @static
10542  */
10543 Roo.data.Record.create = function(o){
10544     var f = function(){
10545         f.superclass.constructor.apply(this, arguments);
10546     };
10547     Roo.extend(f, Roo.data.Record);
10548     var p = f.prototype;
10549     p.fields = new Roo.util.MixedCollection(false, function(field){
10550         return field.name;
10551     });
10552     for(var i = 0, len = o.length; i < len; i++){
10553         p.fields.add(new Roo.data.Field(o[i]));
10554     }
10555     f.getField = function(name){
10556         return p.fields.get(name);  
10557     };
10558     return f;
10559 };
10560
10561 Roo.data.Record.AUTO_ID = 1000;
10562 Roo.data.Record.EDIT = 'edit';
10563 Roo.data.Record.REJECT = 'reject';
10564 Roo.data.Record.COMMIT = 'commit';
10565
10566 Roo.data.Record.prototype = {
10567     /**
10568      * Readonly flag - true if this record has been modified.
10569      * @type Boolean
10570      */
10571     dirty : false,
10572     editing : false,
10573     error: null,
10574     modified: null,
10575
10576     // private
10577     join : function(store){
10578         this.store = store;
10579     },
10580
10581     /**
10582      * Set the named field to the specified value.
10583      * @param {String} name The name of the field to set.
10584      * @param {Object} value The value to set the field to.
10585      */
10586     set : function(name, value){
10587         if(this.data[name] == value){
10588             return;
10589         }
10590         this.dirty = true;
10591         if(!this.modified){
10592             this.modified = {};
10593         }
10594         if(typeof this.modified[name] == 'undefined'){
10595             this.modified[name] = this.data[name];
10596         }
10597         this.data[name] = value;
10598         if(!this.editing && this.store){
10599             this.store.afterEdit(this);
10600         }       
10601     },
10602
10603     /**
10604      * Get the value of the named field.
10605      * @param {String} name The name of the field to get the value of.
10606      * @return {Object} The value of the field.
10607      */
10608     get : function(name){
10609         return this.data[name]; 
10610     },
10611
10612     // private
10613     beginEdit : function(){
10614         this.editing = true;
10615         this.modified = {}; 
10616     },
10617
10618     // private
10619     cancelEdit : function(){
10620         this.editing = false;
10621         delete this.modified;
10622     },
10623
10624     // private
10625     endEdit : function(){
10626         this.editing = false;
10627         if(this.dirty && this.store){
10628             this.store.afterEdit(this);
10629         }
10630     },
10631
10632     /**
10633      * Usually called by the {@link Roo.data.Store} which owns the Record.
10634      * Rejects all changes made to the Record since either creation, or the last commit operation.
10635      * Modified fields are reverted to their original values.
10636      * <p>
10637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10638      * of reject operations.
10639      */
10640     reject : function(){
10641         var m = this.modified;
10642         for(var n in m){
10643             if(typeof m[n] != "function"){
10644                 this.data[n] = m[n];
10645             }
10646         }
10647         this.dirty = false;
10648         delete this.modified;
10649         this.editing = false;
10650         if(this.store){
10651             this.store.afterReject(this);
10652         }
10653     },
10654
10655     /**
10656      * Usually called by the {@link Roo.data.Store} which owns the Record.
10657      * Commits all changes made to the Record since either creation, or the last commit operation.
10658      * <p>
10659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10660      * of commit operations.
10661      */
10662     commit : function(){
10663         this.dirty = false;
10664         delete this.modified;
10665         this.editing = false;
10666         if(this.store){
10667             this.store.afterCommit(this);
10668         }
10669     },
10670
10671     // private
10672     hasError : function(){
10673         return this.error != null;
10674     },
10675
10676     // private
10677     clearError : function(){
10678         this.error = null;
10679     },
10680
10681     /**
10682      * Creates a copy of this record.
10683      * @param {String} id (optional) A new record id if you don't want to use this record's id
10684      * @return {Record}
10685      */
10686     copy : function(newId) {
10687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10688     }
10689 };/*
10690  * Based on:
10691  * Ext JS Library 1.1.1
10692  * Copyright(c) 2006-2007, Ext JS, LLC.
10693  *
10694  * Originally Released Under LGPL - original licence link has changed is not relivant.
10695  *
10696  * Fork - LGPL
10697  * <script type="text/javascript">
10698  */
10699
10700
10701
10702 /**
10703  * @class Roo.data.Store
10704  * @extends Roo.util.Observable
10705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10707  * <p>
10708  * 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
10709  * has no knowledge of the format of the data returned by the Proxy.<br>
10710  * <p>
10711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10712  * instances from the data object. These records are cached and made available through accessor functions.
10713  * @constructor
10714  * Creates a new Store.
10715  * @param {Object} config A config object containing the objects needed for the Store to access data,
10716  * and read the data into Records.
10717  */
10718 Roo.data.Store = function(config){
10719     this.data = new Roo.util.MixedCollection(false);
10720     this.data.getKey = function(o){
10721         return o.id;
10722     };
10723     this.baseParams = {};
10724     // private
10725     this.paramNames = {
10726         "start" : "start",
10727         "limit" : "limit",
10728         "sort" : "sort",
10729         "dir" : "dir",
10730         "multisort" : "_multisort"
10731     };
10732
10733     if(config && config.data){
10734         this.inlineData = config.data;
10735         delete config.data;
10736     }
10737
10738     Roo.apply(this, config);
10739     
10740     if(this.reader){ // reader passed
10741         this.reader = Roo.factory(this.reader, Roo.data);
10742         this.reader.xmodule = this.xmodule || false;
10743         if(!this.recordType){
10744             this.recordType = this.reader.recordType;
10745         }
10746         if(this.reader.onMetaChange){
10747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10748         }
10749     }
10750
10751     if(this.recordType){
10752         this.fields = this.recordType.prototype.fields;
10753     }
10754     this.modified = [];
10755
10756     this.addEvents({
10757         /**
10758          * @event datachanged
10759          * Fires when the data cache has changed, and a widget which is using this Store
10760          * as a Record cache should refresh its view.
10761          * @param {Store} this
10762          */
10763         datachanged : true,
10764         /**
10765          * @event metachange
10766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10767          * @param {Store} this
10768          * @param {Object} meta The JSON metadata
10769          */
10770         metachange : true,
10771         /**
10772          * @event add
10773          * Fires when Records have been added to the Store
10774          * @param {Store} this
10775          * @param {Roo.data.Record[]} records The array of Records added
10776          * @param {Number} index The index at which the record(s) were added
10777          */
10778         add : true,
10779         /**
10780          * @event remove
10781          * Fires when a Record has been removed from the Store
10782          * @param {Store} this
10783          * @param {Roo.data.Record} record The Record that was removed
10784          * @param {Number} index The index at which the record was removed
10785          */
10786         remove : true,
10787         /**
10788          * @event update
10789          * Fires when a Record has been updated
10790          * @param {Store} this
10791          * @param {Roo.data.Record} record The Record that was updated
10792          * @param {String} operation The update operation being performed.  Value may be one of:
10793          * <pre><code>
10794  Roo.data.Record.EDIT
10795  Roo.data.Record.REJECT
10796  Roo.data.Record.COMMIT
10797          * </code></pre>
10798          */
10799         update : true,
10800         /**
10801          * @event clear
10802          * Fires when the data cache has been cleared.
10803          * @param {Store} this
10804          */
10805         clear : true,
10806         /**
10807          * @event beforeload
10808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10809          * the load action will be canceled.
10810          * @param {Store} this
10811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10812          */
10813         beforeload : true,
10814         /**
10815          * @event beforeloadadd
10816          * Fires after a new set of Records has been loaded.
10817          * @param {Store} this
10818          * @param {Roo.data.Record[]} records The Records that were loaded
10819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10820          */
10821         beforeloadadd : true,
10822         /**
10823          * @event load
10824          * Fires after a new set of Records has been loaded, before they are added to the store.
10825          * @param {Store} this
10826          * @param {Roo.data.Record[]} records The Records that were loaded
10827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10828          * @params {Object} return from reader
10829          */
10830         load : true,
10831         /**
10832          * @event loadexception
10833          * Fires if an exception occurs in the Proxy during loading.
10834          * Called with the signature of the Proxy's "loadexception" event.
10835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10836          * 
10837          * @param {Proxy} 
10838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10839          * @param {Object} load options 
10840          * @param {Object} jsonData from your request (normally this contains the Exception)
10841          */
10842         loadexception : true
10843     });
10844     
10845     if(this.proxy){
10846         this.proxy = Roo.factory(this.proxy, Roo.data);
10847         this.proxy.xmodule = this.xmodule || false;
10848         this.relayEvents(this.proxy,  ["loadexception"]);
10849     }
10850     this.sortToggle = {};
10851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10852
10853     Roo.data.Store.superclass.constructor.call(this);
10854
10855     if(this.inlineData){
10856         this.loadData(this.inlineData);
10857         delete this.inlineData;
10858     }
10859 };
10860
10861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10862      /**
10863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10864     * without a remote query - used by combo/forms at present.
10865     */
10866     
10867     /**
10868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10869     */
10870     /**
10871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10872     */
10873     /**
10874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10876     */
10877     /**
10878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10879     * on any HTTP request
10880     */
10881     /**
10882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10883     */
10884     /**
10885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10886     */
10887     multiSort: false,
10888     /**
10889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10891     */
10892     remoteSort : false,
10893
10894     /**
10895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10896      * loaded or when a record is removed. (defaults to false).
10897     */
10898     pruneModifiedRecords : false,
10899
10900     // private
10901     lastOptions : null,
10902
10903     /**
10904      * Add Records to the Store and fires the add event.
10905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10906      */
10907     add : function(records){
10908         records = [].concat(records);
10909         for(var i = 0, len = records.length; i < len; i++){
10910             records[i].join(this);
10911         }
10912         var index = this.data.length;
10913         this.data.addAll(records);
10914         this.fireEvent("add", this, records, index);
10915     },
10916
10917     /**
10918      * Remove a Record from the Store and fires the remove event.
10919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10920      */
10921     remove : function(record){
10922         var index = this.data.indexOf(record);
10923         this.data.removeAt(index);
10924         if(this.pruneModifiedRecords){
10925             this.modified.remove(record);
10926         }
10927         this.fireEvent("remove", this, record, index);
10928     },
10929
10930     /**
10931      * Remove all Records from the Store and fires the clear event.
10932      */
10933     removeAll : function(){
10934         this.data.clear();
10935         if(this.pruneModifiedRecords){
10936             this.modified = [];
10937         }
10938         this.fireEvent("clear", this);
10939     },
10940
10941     /**
10942      * Inserts Records to the Store at the given index and fires the add event.
10943      * @param {Number} index The start index at which to insert the passed Records.
10944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10945      */
10946     insert : function(index, records){
10947         records = [].concat(records);
10948         for(var i = 0, len = records.length; i < len; i++){
10949             this.data.insert(index, records[i]);
10950             records[i].join(this);
10951         }
10952         this.fireEvent("add", this, records, index);
10953     },
10954
10955     /**
10956      * Get the index within the cache of the passed Record.
10957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10958      * @return {Number} The index of the passed Record. Returns -1 if not found.
10959      */
10960     indexOf : function(record){
10961         return this.data.indexOf(record);
10962     },
10963
10964     /**
10965      * Get the index within the cache of the Record with the passed id.
10966      * @param {String} id The id of the Record to find.
10967      * @return {Number} The index of the Record. Returns -1 if not found.
10968      */
10969     indexOfId : function(id){
10970         return this.data.indexOfKey(id);
10971     },
10972
10973     /**
10974      * Get the Record with the specified id.
10975      * @param {String} id The id of the Record to find.
10976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10977      */
10978     getById : function(id){
10979         return this.data.key(id);
10980     },
10981
10982     /**
10983      * Get the Record at the specified index.
10984      * @param {Number} index The index of the Record to find.
10985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10986      */
10987     getAt : function(index){
10988         return this.data.itemAt(index);
10989     },
10990
10991     /**
10992      * Returns a range of Records between specified indices.
10993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10995      * @return {Roo.data.Record[]} An array of Records
10996      */
10997     getRange : function(start, end){
10998         return this.data.getRange(start, end);
10999     },
11000
11001     // private
11002     storeOptions : function(o){
11003         o = Roo.apply({}, o);
11004         delete o.callback;
11005         delete o.scope;
11006         this.lastOptions = o;
11007     },
11008
11009     /**
11010      * Loads the Record cache from the configured Proxy using the configured Reader.
11011      * <p>
11012      * If using remote paging, then the first load call must specify the <em>start</em>
11013      * and <em>limit</em> properties in the options.params property to establish the initial
11014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11015      * <p>
11016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11017      * and this call will return before the new data has been loaded. Perform any post-processing
11018      * in a callback function, or in a "load" event handler.</strong>
11019      * <p>
11020      * @param {Object} options An object containing properties which control loading options:<ul>
11021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11023      * passed the following arguments:<ul>
11024      * <li>r : Roo.data.Record[]</li>
11025      * <li>options: Options object from the load call</li>
11026      * <li>success: Boolean success indicator</li></ul></li>
11027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11029      * </ul>
11030      */
11031     load : function(options){
11032         options = options || {};
11033         if(this.fireEvent("beforeload", this, options) !== false){
11034             this.storeOptions(options);
11035             var p = Roo.apply(options.params || {}, this.baseParams);
11036             // if meta was not loaded from remote source.. try requesting it.
11037             if (!this.reader.metaFromRemote) {
11038                 p._requestMeta = 1;
11039             }
11040             if(this.sortInfo && this.remoteSort){
11041                 var pn = this.paramNames;
11042                 p[pn["sort"]] = this.sortInfo.field;
11043                 p[pn["dir"]] = this.sortInfo.direction;
11044             }
11045             if (this.multiSort) {
11046                 var pn = this.paramNames;
11047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11048             }
11049             
11050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11051         }
11052     },
11053
11054     /**
11055      * Reloads the Record cache from the configured Proxy using the configured Reader and
11056      * the options from the last load operation performed.
11057      * @param {Object} options (optional) An object containing properties which may override the options
11058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11059      * the most recently used options are reused).
11060      */
11061     reload : function(options){
11062         this.load(Roo.applyIf(options||{}, this.lastOptions));
11063     },
11064
11065     // private
11066     // Called as a callback by the Reader during a load operation.
11067     loadRecords : function(o, options, success){
11068         if(!o || success === false){
11069             if(success !== false){
11070                 this.fireEvent("load", this, [], options, o);
11071             }
11072             if(options.callback){
11073                 options.callback.call(options.scope || this, [], options, false);
11074             }
11075             return;
11076         }
11077         // if data returned failure - throw an exception.
11078         if (o.success === false) {
11079             // show a message if no listener is registered.
11080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11082             }
11083             // loadmask wil be hooked into this..
11084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11085             return;
11086         }
11087         var r = o.records, t = o.totalRecords || r.length;
11088         
11089         this.fireEvent("beforeloadadd", this, r, options, o);
11090         
11091         if(!options || options.add !== true){
11092             if(this.pruneModifiedRecords){
11093                 this.modified = [];
11094             }
11095             for(var i = 0, len = r.length; i < len; i++){
11096                 r[i].join(this);
11097             }
11098             if(this.snapshot){
11099                 this.data = this.snapshot;
11100                 delete this.snapshot;
11101             }
11102             this.data.clear();
11103             this.data.addAll(r);
11104             this.totalLength = t;
11105             this.applySort();
11106             this.fireEvent("datachanged", this);
11107         }else{
11108             this.totalLength = Math.max(t, this.data.length+r.length);
11109             this.add(r);
11110         }
11111         
11112         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11113                 
11114             var e = new Roo.data.Record({});
11115
11116             e.set(this.parent.displayField, this.parent.emptyTitle);
11117             e.set(this.parent.valueField, '');
11118
11119             this.insert(0, e);
11120         }
11121             
11122         this.fireEvent("load", this, r, options, o);
11123         if(options.callback){
11124             options.callback.call(options.scope || this, r, options, true);
11125         }
11126     },
11127
11128
11129     /**
11130      * Loads data from a passed data block. A Reader which understands the format of the data
11131      * must have been configured in the constructor.
11132      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11133      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11134      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11135      */
11136     loadData : function(o, append){
11137         var r = this.reader.readRecords(o);
11138         this.loadRecords(r, {add: append}, true);
11139     },
11140
11141     /**
11142      * Gets the number of cached records.
11143      * <p>
11144      * <em>If using paging, this may not be the total size of the dataset. If the data object
11145      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11146      * the data set size</em>
11147      */
11148     getCount : function(){
11149         return this.data.length || 0;
11150     },
11151
11152     /**
11153      * Gets the total number of records in the dataset as returned by the server.
11154      * <p>
11155      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11156      * the dataset size</em>
11157      */
11158     getTotalCount : function(){
11159         return this.totalLength || 0;
11160     },
11161
11162     /**
11163      * Returns the sort state of the Store as an object with two properties:
11164      * <pre><code>
11165  field {String} The name of the field by which the Records are sorted
11166  direction {String} The sort order, "ASC" or "DESC"
11167      * </code></pre>
11168      */
11169     getSortState : function(){
11170         return this.sortInfo;
11171     },
11172
11173     // private
11174     applySort : function(){
11175         if(this.sortInfo && !this.remoteSort){
11176             var s = this.sortInfo, f = s.field;
11177             var st = this.fields.get(f).sortType;
11178             var fn = function(r1, r2){
11179                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11180                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11181             };
11182             this.data.sort(s.direction, fn);
11183             if(this.snapshot && this.snapshot != this.data){
11184                 this.snapshot.sort(s.direction, fn);
11185             }
11186         }
11187     },
11188
11189     /**
11190      * Sets the default sort column and order to be used by the next load operation.
11191      * @param {String} fieldName The name of the field to sort by.
11192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193      */
11194     setDefaultSort : function(field, dir){
11195         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11196     },
11197
11198     /**
11199      * Sort the Records.
11200      * If remote sorting is used, the sort is performed on the server, and the cache is
11201      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
11206         var f = this.fields.get(fieldName);
11207         if(!dir){
11208             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11209             
11210             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11211                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11212             }else{
11213                 dir = f.sortDir;
11214             }
11215         }
11216         this.sortToggle[f.name] = dir;
11217         this.sortInfo = {field: f.name, direction: dir};
11218         if(!this.remoteSort){
11219             this.applySort();
11220             this.fireEvent("datachanged", this);
11221         }else{
11222             this.load(this.lastOptions);
11223         }
11224     },
11225
11226     /**
11227      * Calls the specified function for each of the Records in the cache.
11228      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11229      * Returning <em>false</em> aborts and exits the iteration.
11230      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11231      */
11232     each : function(fn, scope){
11233         this.data.each(fn, scope);
11234     },
11235
11236     /**
11237      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11238      * (e.g., during paging).
11239      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11240      */
11241     getModifiedRecords : function(){
11242         return this.modified;
11243     },
11244
11245     // private
11246     createFilterFn : function(property, value, anyMatch){
11247         if(!value.exec){ // not a regex
11248             value = String(value);
11249             if(value.length == 0){
11250                 return false;
11251             }
11252             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11253         }
11254         return function(r){
11255             return value.test(r.data[property]);
11256         };
11257     },
11258
11259     /**
11260      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11261      * @param {String} property A field on your records
11262      * @param {Number} start The record index to start at (defaults to 0)
11263      * @param {Number} end The last record index to include (defaults to length - 1)
11264      * @return {Number} The sum
11265      */
11266     sum : function(property, start, end){
11267         var rs = this.data.items, v = 0;
11268         start = start || 0;
11269         end = (end || end === 0) ? end : rs.length-1;
11270
11271         for(var i = start; i <= end; i++){
11272             v += (rs[i].data[property] || 0);
11273         }
11274         return v;
11275     },
11276
11277     /**
11278      * Filter the records by a specified property.
11279      * @param {String} field A field on your records
11280      * @param {String/RegExp} value Either a string that the field
11281      * should start with or a RegExp to test against the field
11282      * @param {Boolean} anyMatch True to match any part not just the beginning
11283      */
11284     filter : function(property, value, anyMatch){
11285         var fn = this.createFilterFn(property, value, anyMatch);
11286         return fn ? this.filterBy(fn) : this.clearFilter();
11287     },
11288
11289     /**
11290      * Filter by a function. The specified function will be called with each
11291      * record in this data source. If the function returns true the record is included,
11292      * otherwise it is filtered.
11293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11294      * @param {Object} scope (optional) The scope of the function (defaults to this)
11295      */
11296     filterBy : function(fn, scope){
11297         this.snapshot = this.snapshot || this.data;
11298         this.data = this.queryBy(fn, scope||this);
11299         this.fireEvent("datachanged", this);
11300     },
11301
11302     /**
11303      * Query the records by a specified property.
11304      * @param {String} field A field on your records
11305      * @param {String/RegExp} value Either a string that the field
11306      * should start with or a RegExp to test against the field
11307      * @param {Boolean} anyMatch True to match any part not just the beginning
11308      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      */
11310     query : function(property, value, anyMatch){
11311         var fn = this.createFilterFn(property, value, anyMatch);
11312         return fn ? this.queryBy(fn) : this.data.clone();
11313     },
11314
11315     /**
11316      * Query by a function. The specified function will be called with each
11317      * record in this data source. If the function returns true the record is included
11318      * in the results.
11319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11320      * @param {Object} scope (optional) The scope of the function (defaults to this)
11321       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11322      **/
11323     queryBy : function(fn, scope){
11324         var data = this.snapshot || this.data;
11325         return data.filterBy(fn, scope||this);
11326     },
11327
11328     /**
11329      * Collects unique values for a particular dataIndex from this store.
11330      * @param {String} dataIndex The property to collect
11331      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11332      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11333      * @return {Array} An array of the unique values
11334      **/
11335     collect : function(dataIndex, allowNull, bypassFilter){
11336         var d = (bypassFilter === true && this.snapshot) ?
11337                 this.snapshot.items : this.data.items;
11338         var v, sv, r = [], l = {};
11339         for(var i = 0, len = d.length; i < len; i++){
11340             v = d[i].data[dataIndex];
11341             sv = String(v);
11342             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11343                 l[sv] = true;
11344                 r[r.length] = v;
11345             }
11346         }
11347         return r;
11348     },
11349
11350     /**
11351      * Revert to a view of the Record cache with no filtering applied.
11352      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11353      */
11354     clearFilter : function(suppressEvent){
11355         if(this.snapshot && this.snapshot != this.data){
11356             this.data = this.snapshot;
11357             delete this.snapshot;
11358             if(suppressEvent !== true){
11359                 this.fireEvent("datachanged", this);
11360             }
11361         }
11362     },
11363
11364     // private
11365     afterEdit : function(record){
11366         if(this.modified.indexOf(record) == -1){
11367             this.modified.push(record);
11368         }
11369         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11370     },
11371     
11372     // private
11373     afterReject : function(record){
11374         this.modified.remove(record);
11375         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11376     },
11377
11378     // private
11379     afterCommit : function(record){
11380         this.modified.remove(record);
11381         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11382     },
11383
11384     /**
11385      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11386      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11387      */
11388     commitChanges : function(){
11389         var m = this.modified.slice(0);
11390         this.modified = [];
11391         for(var i = 0, len = m.length; i < len; i++){
11392             m[i].commit();
11393         }
11394     },
11395
11396     /**
11397      * Cancel outstanding changes on all changed records.
11398      */
11399     rejectChanges : 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].reject();
11404         }
11405     },
11406
11407     onMetaChange : function(meta, rtype, o){
11408         this.recordType = rtype;
11409         this.fields = rtype.prototype.fields;
11410         delete this.snapshot;
11411         this.sortInfo = meta.sortInfo || this.sortInfo;
11412         this.modified = [];
11413         this.fireEvent('metachange', this, this.reader.meta);
11414     },
11415     
11416     moveIndex : function(data, type)
11417     {
11418         var index = this.indexOf(data);
11419         
11420         var newIndex = index + type;
11421         
11422         this.remove(data);
11423         
11424         this.insert(newIndex, data);
11425         
11426     }
11427 });/*
11428  * Based on:
11429  * Ext JS Library 1.1.1
11430  * Copyright(c) 2006-2007, Ext JS, LLC.
11431  *
11432  * Originally Released Under LGPL - original licence link has changed is not relivant.
11433  *
11434  * Fork - LGPL
11435  * <script type="text/javascript">
11436  */
11437
11438 /**
11439  * @class Roo.data.SimpleStore
11440  * @extends Roo.data.Store
11441  * Small helper class to make creating Stores from Array data easier.
11442  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11443  * @cfg {Array} fields An array of field definition objects, or field name strings.
11444  * @cfg {Array} data The multi-dimensional array of data
11445  * @constructor
11446  * @param {Object} config
11447  */
11448 Roo.data.SimpleStore = function(config){
11449     Roo.data.SimpleStore.superclass.constructor.call(this, {
11450         isLocal : true,
11451         reader: new Roo.data.ArrayReader({
11452                 id: config.id
11453             },
11454             Roo.data.Record.create(config.fields)
11455         ),
11456         proxy : new Roo.data.MemoryProxy(config.data)
11457     });
11458     this.load();
11459 };
11460 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471 /**
11472 /**
11473  * @extends Roo.data.Store
11474  * @class Roo.data.JsonStore
11475  * Small helper class to make creating Stores for JSON data easier. <br/>
11476 <pre><code>
11477 var store = new Roo.data.JsonStore({
11478     url: 'get-images.php',
11479     root: 'images',
11480     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11481 });
11482 </code></pre>
11483  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11484  * JsonReader and HttpProxy (unless inline data is provided).</b>
11485  * @cfg {Array} fields An array of field definition objects, or field name strings.
11486  * @constructor
11487  * @param {Object} config
11488  */
11489 Roo.data.JsonStore = function(c){
11490     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11491         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11492         reader: new Roo.data.JsonReader(c, c.fields)
11493     }));
11494 };
11495 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11496  * Based on:
11497  * Ext JS Library 1.1.1
11498  * Copyright(c) 2006-2007, Ext JS, LLC.
11499  *
11500  * Originally Released Under LGPL - original licence link has changed is not relivant.
11501  *
11502  * Fork - LGPL
11503  * <script type="text/javascript">
11504  */
11505
11506  
11507 Roo.data.Field = function(config){
11508     if(typeof config == "string"){
11509         config = {name: config};
11510     }
11511     Roo.apply(this, config);
11512     
11513     if(!this.type){
11514         this.type = "auto";
11515     }
11516     
11517     var st = Roo.data.SortTypes;
11518     // named sortTypes are supported, here we look them up
11519     if(typeof this.sortType == "string"){
11520         this.sortType = st[this.sortType];
11521     }
11522     
11523     // set default sortType for strings and dates
11524     if(!this.sortType){
11525         switch(this.type){
11526             case "string":
11527                 this.sortType = st.asUCString;
11528                 break;
11529             case "date":
11530                 this.sortType = st.asDate;
11531                 break;
11532             default:
11533                 this.sortType = st.none;
11534         }
11535     }
11536
11537     // define once
11538     var stripRe = /[\$,%]/g;
11539
11540     // prebuilt conversion function for this field, instead of
11541     // switching every time we're reading a value
11542     if(!this.convert){
11543         var cv, dateFormat = this.dateFormat;
11544         switch(this.type){
11545             case "":
11546             case "auto":
11547             case undefined:
11548                 cv = function(v){ return v; };
11549                 break;
11550             case "string":
11551                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11552                 break;
11553             case "int":
11554                 cv = function(v){
11555                     return v !== undefined && v !== null && v !== '' ?
11556                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11557                     };
11558                 break;
11559             case "float":
11560                 cv = function(v){
11561                     return v !== undefined && v !== null && v !== '' ?
11562                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11563                     };
11564                 break;
11565             case "bool":
11566             case "boolean":
11567                 cv = function(v){ return v === true || v === "true" || v == 1; };
11568                 break;
11569             case "date":
11570                 cv = function(v){
11571                     if(!v){
11572                         return '';
11573                     }
11574                     if(v instanceof Date){
11575                         return v;
11576                     }
11577                     if(dateFormat){
11578                         if(dateFormat == "timestamp"){
11579                             return new Date(v*1000);
11580                         }
11581                         return Date.parseDate(v, dateFormat);
11582                     }
11583                     var parsed = Date.parse(v);
11584                     return parsed ? new Date(parsed) : null;
11585                 };
11586              break;
11587             
11588         }
11589         this.convert = cv;
11590     }
11591 };
11592
11593 Roo.data.Field.prototype = {
11594     dateFormat: null,
11595     defaultValue: "",
11596     mapping: null,
11597     sortType : null,
11598     sortDir : "ASC"
11599 };/*
11600  * Based on:
11601  * Ext JS Library 1.1.1
11602  * Copyright(c) 2006-2007, Ext JS, LLC.
11603  *
11604  * Originally Released Under LGPL - original licence link has changed is not relivant.
11605  *
11606  * Fork - LGPL
11607  * <script type="text/javascript">
11608  */
11609  
11610 // Base class for reading structured data from a data source.  This class is intended to be
11611 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11612
11613 /**
11614  * @class Roo.data.DataReader
11615  * Base class for reading structured data from a data source.  This class is intended to be
11616  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11617  */
11618
11619 Roo.data.DataReader = function(meta, recordType){
11620     
11621     this.meta = meta;
11622     
11623     this.recordType = recordType instanceof Array ? 
11624         Roo.data.Record.create(recordType) : recordType;
11625 };
11626
11627 Roo.data.DataReader.prototype = {
11628      /**
11629      * Create an empty record
11630      * @param {Object} data (optional) - overlay some values
11631      * @return {Roo.data.Record} record created.
11632      */
11633     newRow :  function(d) {
11634         var da =  {};
11635         this.recordType.prototype.fields.each(function(c) {
11636             switch( c.type) {
11637                 case 'int' : da[c.name] = 0; break;
11638                 case 'date' : da[c.name] = new Date(); break;
11639                 case 'float' : da[c.name] = 0.0; break;
11640                 case 'boolean' : da[c.name] = false; break;
11641                 default : da[c.name] = ""; break;
11642             }
11643             
11644         });
11645         return new this.recordType(Roo.apply(da, d));
11646     }
11647     
11648 };/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660  * @class Roo.data.DataProxy
11661  * @extends Roo.data.Observable
11662  * This class is an abstract base class for implementations which provide retrieval of
11663  * unformatted data objects.<br>
11664  * <p>
11665  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11666  * (of the appropriate type which knows how to parse the data object) to provide a block of
11667  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11668  * <p>
11669  * Custom implementations must implement the load method as described in
11670  * {@link Roo.data.HttpProxy#load}.
11671  */
11672 Roo.data.DataProxy = function(){
11673     this.addEvents({
11674         /**
11675          * @event beforeload
11676          * Fires before a network request is made to retrieve a data object.
11677          * @param {Object} This DataProxy object.
11678          * @param {Object} params The params parameter to the load function.
11679          */
11680         beforeload : true,
11681         /**
11682          * @event load
11683          * Fires before the load method's callback is called.
11684          * @param {Object} This DataProxy object.
11685          * @param {Object} o The data object.
11686          * @param {Object} arg The callback argument object passed to the load function.
11687          */
11688         load : true,
11689         /**
11690          * @event loadexception
11691          * Fires if an Exception occurs during data retrieval.
11692          * @param {Object} This DataProxy object.
11693          * @param {Object} o The data object.
11694          * @param {Object} arg The callback argument object passed to the load function.
11695          * @param {Object} e The Exception.
11696          */
11697         loadexception : true
11698     });
11699     Roo.data.DataProxy.superclass.constructor.call(this);
11700 };
11701
11702 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11703
11704     /**
11705      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11706      */
11707 /*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717 /**
11718  * @class Roo.data.MemoryProxy
11719  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11720  * to the Reader when its load method is called.
11721  * @constructor
11722  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11723  */
11724 Roo.data.MemoryProxy = function(data){
11725     if (data.data) {
11726         data = data.data;
11727     }
11728     Roo.data.MemoryProxy.superclass.constructor.call(this);
11729     this.data = data;
11730 };
11731
11732 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11733     
11734     /**
11735      * Load data from the requested source (in this case an in-memory
11736      * data object passed to the constructor), read the data object into
11737      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11738      * process that block using the passed callback.
11739      * @param {Object} params This parameter is not used by the MemoryProxy class.
11740      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11741      * object into a block of Roo.data.Records.
11742      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11743      * The function must be passed <ul>
11744      * <li>The Record block object</li>
11745      * <li>The "arg" argument from the load function</li>
11746      * <li>A boolean success indicator</li>
11747      * </ul>
11748      * @param {Object} scope The scope in which to call the callback
11749      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11750      */
11751     load : function(params, reader, callback, scope, arg){
11752         params = params || {};
11753         var result;
11754         try {
11755             result = reader.readRecords(this.data);
11756         }catch(e){
11757             this.fireEvent("loadexception", this, arg, null, e);
11758             callback.call(scope, null, arg, false);
11759             return;
11760         }
11761         callback.call(scope, result, arg, true);
11762     },
11763     
11764     // private
11765     update : function(params, records){
11766         
11767     }
11768 });/*
11769  * Based on:
11770  * Ext JS Library 1.1.1
11771  * Copyright(c) 2006-2007, Ext JS, LLC.
11772  *
11773  * Originally Released Under LGPL - original licence link has changed is not relivant.
11774  *
11775  * Fork - LGPL
11776  * <script type="text/javascript">
11777  */
11778 /**
11779  * @class Roo.data.HttpProxy
11780  * @extends Roo.data.DataProxy
11781  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11782  * configured to reference a certain URL.<br><br>
11783  * <p>
11784  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11785  * from which the running page was served.<br><br>
11786  * <p>
11787  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11788  * <p>
11789  * Be aware that to enable the browser to parse an XML document, the server must set
11790  * the Content-Type header in the HTTP response to "text/xml".
11791  * @constructor
11792  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11793  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11794  * will be used to make the request.
11795  */
11796 Roo.data.HttpProxy = function(conn){
11797     Roo.data.HttpProxy.superclass.constructor.call(this);
11798     // is conn a conn config or a real conn?
11799     this.conn = conn;
11800     this.useAjax = !conn || !conn.events;
11801   
11802 };
11803
11804 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11805     // thse are take from connection...
11806     
11807     /**
11808      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11809      */
11810     /**
11811      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11812      * extra parameters to each request made by this object. (defaults to undefined)
11813      */
11814     /**
11815      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11816      *  to each request made by this object. (defaults to undefined)
11817      */
11818     /**
11819      * @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)
11820      */
11821     /**
11822      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11823      */
11824      /**
11825      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11826      * @type Boolean
11827      */
11828   
11829
11830     /**
11831      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11832      * @type Boolean
11833      */
11834     /**
11835      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11836      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11837      * a finer-grained basis than the DataProxy events.
11838      */
11839     getConnection : function(){
11840         return this.useAjax ? Roo.Ajax : this.conn;
11841     },
11842
11843     /**
11844      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11845      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11846      * process that block using the passed callback.
11847      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11848      * for the request to the remote server.
11849      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11850      * object into a block of Roo.data.Records.
11851      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11852      * The function must be passed <ul>
11853      * <li>The Record block object</li>
11854      * <li>The "arg" argument from the load function</li>
11855      * <li>A boolean success indicator</li>
11856      * </ul>
11857      * @param {Object} scope The scope in which to call the callback
11858      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11859      */
11860     load : function(params, reader, callback, scope, arg){
11861         if(this.fireEvent("beforeload", this, params) !== false){
11862             var  o = {
11863                 params : params || {},
11864                 request: {
11865                     callback : callback,
11866                     scope : scope,
11867                     arg : arg
11868                 },
11869                 reader: reader,
11870                 callback : this.loadResponse,
11871                 scope: this
11872             };
11873             if(this.useAjax){
11874                 Roo.applyIf(o, this.conn);
11875                 if(this.activeRequest){
11876                     Roo.Ajax.abort(this.activeRequest);
11877                 }
11878                 this.activeRequest = Roo.Ajax.request(o);
11879             }else{
11880                 this.conn.request(o);
11881             }
11882         }else{
11883             callback.call(scope||this, null, arg, false);
11884         }
11885     },
11886
11887     // private
11888     loadResponse : function(o, success, response){
11889         delete this.activeRequest;
11890         if(!success){
11891             this.fireEvent("loadexception", this, o, response);
11892             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11893             return;
11894         }
11895         var result;
11896         try {
11897             result = o.reader.read(response);
11898         }catch(e){
11899             this.fireEvent("loadexception", this, o, response, e);
11900             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11901             return;
11902         }
11903         
11904         this.fireEvent("load", this, o, o.request.arg);
11905         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11906     },
11907
11908     // private
11909     update : function(dataSet){
11910
11911     },
11912
11913     // private
11914     updateResponse : function(dataSet){
11915
11916     }
11917 });/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.ScriptTagProxy
11930  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11931  * other than the originating domain of the running page.<br><br>
11932  * <p>
11933  * <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
11934  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11935  * <p>
11936  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11937  * source code that is used as the source inside a &lt;script> tag.<br><br>
11938  * <p>
11939  * In order for the browser to process the returned data, the server must wrap the data object
11940  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11941  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11942  * depending on whether the callback name was passed:
11943  * <p>
11944  * <pre><code>
11945 boolean scriptTag = false;
11946 String cb = request.getParameter("callback");
11947 if (cb != null) {
11948     scriptTag = true;
11949     response.setContentType("text/javascript");
11950 } else {
11951     response.setContentType("application/x-json");
11952 }
11953 Writer out = response.getWriter();
11954 if (scriptTag) {
11955     out.write(cb + "(");
11956 }
11957 out.print(dataBlock.toJsonString());
11958 if (scriptTag) {
11959     out.write(");");
11960 }
11961 </pre></code>
11962  *
11963  * @constructor
11964  * @param {Object} config A configuration object.
11965  */
11966 Roo.data.ScriptTagProxy = function(config){
11967     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11968     Roo.apply(this, config);
11969     this.head = document.getElementsByTagName("head")[0];
11970 };
11971
11972 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11973
11974 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11975     /**
11976      * @cfg {String} url The URL from which to request the data object.
11977      */
11978     /**
11979      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11980      */
11981     timeout : 30000,
11982     /**
11983      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11984      * the server the name of the callback function set up by the load call to process the returned data object.
11985      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11986      * javascript output which calls this named function passing the data object as its only parameter.
11987      */
11988     callbackParam : "callback",
11989     /**
11990      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11991      * name to the request.
11992      */
11993     nocache : true,
11994
11995     /**
11996      * Load data from the configured URL, read the data object into
11997      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11998      * process that block using the passed callback.
11999      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12000      * for the request to the remote server.
12001      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12002      * object into a block of Roo.data.Records.
12003      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12004      * The function must be passed <ul>
12005      * <li>The Record block object</li>
12006      * <li>The "arg" argument from the load function</li>
12007      * <li>A boolean success indicator</li>
12008      * </ul>
12009      * @param {Object} scope The scope in which to call the callback
12010      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12011      */
12012     load : function(params, reader, callback, scope, arg){
12013         if(this.fireEvent("beforeload", this, params) !== false){
12014
12015             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12016
12017             var url = this.url;
12018             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12019             if(this.nocache){
12020                 url += "&_dc=" + (new Date().getTime());
12021             }
12022             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12023             var trans = {
12024                 id : transId,
12025                 cb : "stcCallback"+transId,
12026                 scriptId : "stcScript"+transId,
12027                 params : params,
12028                 arg : arg,
12029                 url : url,
12030                 callback : callback,
12031                 scope : scope,
12032                 reader : reader
12033             };
12034             var conn = this;
12035
12036             window[trans.cb] = function(o){
12037                 conn.handleResponse(o, trans);
12038             };
12039
12040             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12041
12042             if(this.autoAbort !== false){
12043                 this.abort();
12044             }
12045
12046             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12047
12048             var script = document.createElement("script");
12049             script.setAttribute("src", url);
12050             script.setAttribute("type", "text/javascript");
12051             script.setAttribute("id", trans.scriptId);
12052             this.head.appendChild(script);
12053
12054             this.trans = trans;
12055         }else{
12056             callback.call(scope||this, null, arg, false);
12057         }
12058     },
12059
12060     // private
12061     isLoading : function(){
12062         return this.trans ? true : false;
12063     },
12064
12065     /**
12066      * Abort the current server request.
12067      */
12068     abort : function(){
12069         if(this.isLoading()){
12070             this.destroyTrans(this.trans);
12071         }
12072     },
12073
12074     // private
12075     destroyTrans : function(trans, isLoaded){
12076         this.head.removeChild(document.getElementById(trans.scriptId));
12077         clearTimeout(trans.timeoutId);
12078         if(isLoaded){
12079             window[trans.cb] = undefined;
12080             try{
12081                 delete window[trans.cb];
12082             }catch(e){}
12083         }else{
12084             // if hasn't been loaded, wait for load to remove it to prevent script error
12085             window[trans.cb] = function(){
12086                 window[trans.cb] = undefined;
12087                 try{
12088                     delete window[trans.cb];
12089                 }catch(e){}
12090             };
12091         }
12092     },
12093
12094     // private
12095     handleResponse : function(o, trans){
12096         this.trans = false;
12097         this.destroyTrans(trans, true);
12098         var result;
12099         try {
12100             result = trans.reader.readRecords(o);
12101         }catch(e){
12102             this.fireEvent("loadexception", this, o, trans.arg, e);
12103             trans.callback.call(trans.scope||window, null, trans.arg, false);
12104             return;
12105         }
12106         this.fireEvent("load", this, o, trans.arg);
12107         trans.callback.call(trans.scope||window, result, trans.arg, true);
12108     },
12109
12110     // private
12111     handleFailure : function(trans){
12112         this.trans = false;
12113         this.destroyTrans(trans, false);
12114         this.fireEvent("loadexception", this, null, trans.arg);
12115         trans.callback.call(trans.scope||window, null, trans.arg, false);
12116     }
12117 });/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128 /**
12129  * @class Roo.data.JsonReader
12130  * @extends Roo.data.DataReader
12131  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12132  * based on mappings in a provided Roo.data.Record constructor.
12133  * 
12134  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12135  * in the reply previously. 
12136  * 
12137  * <p>
12138  * Example code:
12139  * <pre><code>
12140 var RecordDef = Roo.data.Record.create([
12141     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12142     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12143 ]);
12144 var myReader = new Roo.data.JsonReader({
12145     totalProperty: "results",    // The property which contains the total dataset size (optional)
12146     root: "rows",                // The property which contains an Array of row objects
12147     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12148 }, RecordDef);
12149 </code></pre>
12150  * <p>
12151  * This would consume a JSON file like this:
12152  * <pre><code>
12153 { 'results': 2, 'rows': [
12154     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12155     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12156 }
12157 </code></pre>
12158  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12159  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12160  * paged from the remote server.
12161  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12162  * @cfg {String} root name of the property which contains the Array of row objects.
12163  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12164  * @cfg {Array} fields Array of field definition objects
12165  * @constructor
12166  * Create a new JsonReader
12167  * @param {Object} meta Metadata configuration options
12168  * @param {Object} recordType Either an Array of field definition objects,
12169  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12170  */
12171 Roo.data.JsonReader = function(meta, recordType){
12172     
12173     meta = meta || {};
12174     // set some defaults:
12175     Roo.applyIf(meta, {
12176         totalProperty: 'total',
12177         successProperty : 'success',
12178         root : 'data',
12179         id : 'id'
12180     });
12181     
12182     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12183 };
12184 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12185     
12186     /**
12187      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12188      * Used by Store query builder to append _requestMeta to params.
12189      * 
12190      */
12191     metaFromRemote : false,
12192     /**
12193      * This method is only used by a DataProxy which has retrieved data from a remote server.
12194      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12195      * @return {Object} data A data block which is used by an Roo.data.Store object as
12196      * a cache of Roo.data.Records.
12197      */
12198     read : function(response){
12199         var json = response.responseText;
12200        
12201         var o = /* eval:var:o */ eval("("+json+")");
12202         if(!o) {
12203             throw {message: "JsonReader.read: Json object not found"};
12204         }
12205         
12206         if(o.metaData){
12207             
12208             delete this.ef;
12209             this.metaFromRemote = true;
12210             this.meta = o.metaData;
12211             this.recordType = Roo.data.Record.create(o.metaData.fields);
12212             this.onMetaChange(this.meta, this.recordType, o);
12213         }
12214         return this.readRecords(o);
12215     },
12216
12217     // private function a store will implement
12218     onMetaChange : function(meta, recordType, o){
12219
12220     },
12221
12222     /**
12223          * @ignore
12224          */
12225     simpleAccess: function(obj, subsc) {
12226         return obj[subsc];
12227     },
12228
12229         /**
12230          * @ignore
12231          */
12232     getJsonAccessor: function(){
12233         var re = /[\[\.]/;
12234         return function(expr) {
12235             try {
12236                 return(re.test(expr))
12237                     ? new Function("obj", "return obj." + expr)
12238                     : function(obj){
12239                         return obj[expr];
12240                     };
12241             } catch(e){}
12242             return Roo.emptyFn;
12243         };
12244     }(),
12245
12246     /**
12247      * Create a data block containing Roo.data.Records from an XML document.
12248      * @param {Object} o An object which contains an Array of row objects in the property specified
12249      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12250      * which contains the total size of the dataset.
12251      * @return {Object} data A data block which is used by an Roo.data.Store object as
12252      * a cache of Roo.data.Records.
12253      */
12254     readRecords : function(o){
12255         /**
12256          * After any data loads, the raw JSON data is available for further custom processing.
12257          * @type Object
12258          */
12259         this.o = o;
12260         var s = this.meta, Record = this.recordType,
12261             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12262
12263 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12264         if (!this.ef) {
12265             if(s.totalProperty) {
12266                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12267                 }
12268                 if(s.successProperty) {
12269                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12270                 }
12271                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12272                 if (s.id) {
12273                         var g = this.getJsonAccessor(s.id);
12274                         this.getId = function(rec) {
12275                                 var r = g(rec);  
12276                                 return (r === undefined || r === "") ? null : r;
12277                         };
12278                 } else {
12279                         this.getId = function(){return null;};
12280                 }
12281             this.ef = [];
12282             for(var jj = 0; jj < fl; jj++){
12283                 f = fi[jj];
12284                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12285                 this.ef[jj] = this.getJsonAccessor(map);
12286             }
12287         }
12288
12289         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12290         if(s.totalProperty){
12291             var vt = parseInt(this.getTotal(o), 10);
12292             if(!isNaN(vt)){
12293                 totalRecords = vt;
12294             }
12295         }
12296         if(s.successProperty){
12297             var vs = this.getSuccess(o);
12298             if(vs === false || vs === 'false'){
12299                 success = false;
12300             }
12301         }
12302         var records = [];
12303         for(var i = 0; i < c; i++){
12304                 var n = root[i];
12305             var values = {};
12306             var id = this.getId(n);
12307             for(var j = 0; j < fl; j++){
12308                 f = fi[j];
12309             var v = this.ef[j](n);
12310             if (!f.convert) {
12311                 Roo.log('missing convert for ' + f.name);
12312                 Roo.log(f);
12313                 continue;
12314             }
12315             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12316             }
12317             var record = new Record(values, id);
12318             record.json = n;
12319             records[i] = record;
12320         }
12321         return {
12322             raw : o,
12323             success : success,
12324             records : records,
12325             totalRecords : totalRecords
12326         };
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.ArrayReader
12341  * @extends Roo.data.DataReader
12342  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12343  * Each element of that Array represents a row of data fields. The
12344  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12345  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12346  * <p>
12347  * Example code:.
12348  * <pre><code>
12349 var RecordDef = Roo.data.Record.create([
12350     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12351     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12352 ]);
12353 var myReader = new Roo.data.ArrayReader({
12354     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12355 }, RecordDef);
12356 </code></pre>
12357  * <p>
12358  * This would consume an Array like this:
12359  * <pre><code>
12360 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12361   </code></pre>
12362  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12363  * @constructor
12364  * Create a new JsonReader
12365  * @param {Object} meta Metadata configuration options.
12366  * @param {Object} recordType Either an Array of field definition objects
12367  * as specified to {@link Roo.data.Record#create},
12368  * or an {@link Roo.data.Record} object
12369  * created using {@link Roo.data.Record#create}.
12370  */
12371 Roo.data.ArrayReader = function(meta, recordType){
12372     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12373 };
12374
12375 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12376     /**
12377      * Create a data block containing Roo.data.Records from an XML document.
12378      * @param {Object} o An Array of row objects which represents the dataset.
12379      * @return {Object} data A data block which is used by an Roo.data.Store object as
12380      * a cache of Roo.data.Records.
12381      */
12382     readRecords : function(o){
12383         var sid = this.meta ? this.meta.id : null;
12384         var recordType = this.recordType, fields = recordType.prototype.fields;
12385         var records = [];
12386         var root = o;
12387             for(var i = 0; i < root.length; i++){
12388                     var n = root[i];
12389                 var values = {};
12390                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12391                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12392                 var f = fields.items[j];
12393                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12394                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12395                 v = f.convert(v);
12396                 values[f.name] = v;
12397             }
12398                 var record = new recordType(values, id);
12399                 record.json = n;
12400                 records[records.length] = record;
12401             }
12402             return {
12403                 records : records,
12404                 totalRecords : records.length
12405             };
12406     }
12407 });/*
12408  * - LGPL
12409  * * 
12410  */
12411
12412 /**
12413  * @class Roo.bootstrap.ComboBox
12414  * @extends Roo.bootstrap.TriggerField
12415  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12416  * @cfg {Boolean} append (true|false) default false
12417  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12418  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12419  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12420  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12421  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12422  * @cfg {Boolean} animate default true
12423  * @cfg {Boolean} emptyResultText only for touch device
12424  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12425  * @cfg {String} emptyTitle default ''
12426  * @constructor
12427  * Create a new ComboBox.
12428  * @param {Object} config Configuration options
12429  */
12430 Roo.bootstrap.ComboBox = function(config){
12431     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12432     this.addEvents({
12433         /**
12434          * @event expand
12435          * Fires when the dropdown list is expanded
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         */
12438         'expand' : true,
12439         /**
12440          * @event collapse
12441          * Fires when the dropdown list is collapsed
12442         * @param {Roo.bootstrap.ComboBox} combo This combo box
12443         */
12444         'collapse' : true,
12445         /**
12446          * @event beforeselect
12447          * Fires before a list item is selected. Return false to cancel the selection.
12448         * @param {Roo.bootstrap.ComboBox} combo This combo box
12449         * @param {Roo.data.Record} record The data record returned from the underlying store
12450         * @param {Number} index The index of the selected item in the dropdown list
12451         */
12452         'beforeselect' : true,
12453         /**
12454          * @event select
12455          * Fires when a list item is selected
12456         * @param {Roo.bootstrap.ComboBox} combo This combo box
12457         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12458         * @param {Number} index The index of the selected item in the dropdown list
12459         */
12460         'select' : true,
12461         /**
12462          * @event beforequery
12463          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12464          * The event object passed has these properties:
12465         * @param {Roo.bootstrap.ComboBox} combo This combo box
12466         * @param {String} query The query
12467         * @param {Boolean} forceAll true to force "all" query
12468         * @param {Boolean} cancel true to cancel the query
12469         * @param {Object} e The query event object
12470         */
12471         'beforequery': true,
12472          /**
12473          * @event add
12474          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'add' : true,
12478         /**
12479          * @event edit
12480          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12483         */
12484         'edit' : true,
12485         /**
12486          * @event remove
12487          * Fires when the remove value from the combobox array
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         */
12490         'remove' : true,
12491         /**
12492          * @event afterremove
12493          * Fires when the remove value from the combobox array
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         */
12496         'afterremove' : true,
12497         /**
12498          * @event specialfilter
12499          * Fires when specialfilter
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             */
12502         'specialfilter' : true,
12503         /**
12504          * @event tick
12505          * Fires when tick the element
12506             * @param {Roo.bootstrap.ComboBox} combo This combo box
12507             */
12508         'tick' : true,
12509         /**
12510          * @event touchviewdisplay
12511          * Fires when touch view require special display (default is using displayField)
12512             * @param {Roo.bootstrap.ComboBox} combo This combo box
12513             * @param {Object} cfg set html .
12514             */
12515         'touchviewdisplay' : true
12516         
12517     });
12518     
12519     this.item = [];
12520     this.tickItems = [];
12521     
12522     this.selectedIndex = -1;
12523     if(this.mode == 'local'){
12524         if(config.queryDelay === undefined){
12525             this.queryDelay = 10;
12526         }
12527         if(config.minChars === undefined){
12528             this.minChars = 0;
12529         }
12530     }
12531 };
12532
12533 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12534      
12535     /**
12536      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12537      * rendering into an Roo.Editor, defaults to false)
12538      */
12539     /**
12540      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12541      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12542      */
12543     /**
12544      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12545      */
12546     /**
12547      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12548      * the dropdown list (defaults to undefined, with no header element)
12549      */
12550
12551      /**
12552      * @cfg {String/Roo.Template} tpl The template to use to render the output
12553      */
12554      
12555      /**
12556      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12557      */
12558     listWidth: undefined,
12559     /**
12560      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12561      * mode = 'remote' or 'text' if mode = 'local')
12562      */
12563     displayField: undefined,
12564     
12565     /**
12566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12567      * mode = 'remote' or 'value' if mode = 'local'). 
12568      * Note: use of a valueField requires the user make a selection
12569      * in order for a value to be mapped.
12570      */
12571     valueField: undefined,
12572     /**
12573      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12574      */
12575     modalTitle : '',
12576     
12577     /**
12578      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12579      * field's data value (defaults to the underlying DOM element's name)
12580      */
12581     hiddenName: undefined,
12582     /**
12583      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12584      */
12585     listClass: '',
12586     /**
12587      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12588      */
12589     selectedClass: 'active',
12590     
12591     /**
12592      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12593      */
12594     shadow:'sides',
12595     /**
12596      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12597      * anchor positions (defaults to 'tl-bl')
12598      */
12599     listAlign: 'tl-bl?',
12600     /**
12601      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12602      */
12603     maxHeight: 300,
12604     /**
12605      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12606      * query specified by the allQuery config option (defaults to 'query')
12607      */
12608     triggerAction: 'query',
12609     /**
12610      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12611      * (defaults to 4, does not apply if editable = false)
12612      */
12613     minChars : 4,
12614     /**
12615      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12616      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12617      */
12618     typeAhead: false,
12619     /**
12620      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12621      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12622      */
12623     queryDelay: 500,
12624     /**
12625      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12626      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12627      */
12628     pageSize: 0,
12629     /**
12630      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12631      * when editable = true (defaults to false)
12632      */
12633     selectOnFocus:false,
12634     /**
12635      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12636      */
12637     queryParam: 'query',
12638     /**
12639      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12640      * when mode = 'remote' (defaults to 'Loading...')
12641      */
12642     loadingText: 'Loading...',
12643     /**
12644      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12645      */
12646     resizable: false,
12647     /**
12648      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12649      */
12650     handleHeight : 8,
12651     /**
12652      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12653      * traditional select (defaults to true)
12654      */
12655     editable: true,
12656     /**
12657      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12658      */
12659     allQuery: '',
12660     /**
12661      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12662      */
12663     mode: 'remote',
12664     /**
12665      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12666      * listWidth has a higher value)
12667      */
12668     minListWidth : 70,
12669     /**
12670      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12671      * allow the user to set arbitrary text into the field (defaults to false)
12672      */
12673     forceSelection:false,
12674     /**
12675      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12676      * if typeAhead = true (defaults to 250)
12677      */
12678     typeAheadDelay : 250,
12679     /**
12680      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12681      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12682      */
12683     valueNotFoundText : undefined,
12684     /**
12685      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12686      */
12687     blockFocus : false,
12688     
12689     /**
12690      * @cfg {Boolean} disableClear Disable showing of clear button.
12691      */
12692     disableClear : false,
12693     /**
12694      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12695      */
12696     alwaysQuery : false,
12697     
12698     /**
12699      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12700      */
12701     multiple : false,
12702     
12703     /**
12704      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12705      */
12706     invalidClass : "has-warning",
12707     
12708     /**
12709      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12710      */
12711     validClass : "has-success",
12712     
12713     /**
12714      * @cfg {Boolean} specialFilter (true|false) special filter default false
12715      */
12716     specialFilter : false,
12717     
12718     /**
12719      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12720      */
12721     mobileTouchView : true,
12722     
12723     /**
12724      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12725      */
12726     useNativeIOS : false,
12727     
12728     ios_options : false,
12729     
12730     //private
12731     addicon : false,
12732     editicon: false,
12733     
12734     page: 0,
12735     hasQuery: false,
12736     append: false,
12737     loadNext: false,
12738     autoFocus : true,
12739     tickable : false,
12740     btnPosition : 'right',
12741     triggerList : true,
12742     showToggleBtn : true,
12743     animate : true,
12744     emptyResultText: 'Empty',
12745     triggerText : 'Select',
12746     emptyTitle : '',
12747     
12748     // element that contains real text value.. (when hidden is used..)
12749     
12750     getAutoCreate : function()
12751     {   
12752         var cfg = false;
12753         //render
12754         /*
12755          * Render classic select for iso
12756          */
12757         
12758         if(Roo.isIOS && this.useNativeIOS){
12759             cfg = this.getAutoCreateNativeIOS();
12760             return cfg;
12761         }
12762         
12763         /*
12764          * Touch Devices
12765          */
12766         
12767         if(Roo.isTouch && this.mobileTouchView){
12768             cfg = this.getAutoCreateTouchView();
12769             return cfg;;
12770         }
12771         
12772         /*
12773          *  Normal ComboBox
12774          */
12775         if(!this.tickable){
12776             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12777             return cfg;
12778         }
12779         
12780         /*
12781          *  ComboBox with tickable selections
12782          */
12783              
12784         var align = this.labelAlign || this.parentLabelAlign();
12785         
12786         cfg = {
12787             cls : 'form-group roo-combobox-tickable' //input-group
12788         };
12789         
12790         var btn_text_select = '';
12791         var btn_text_done = '';
12792         var btn_text_cancel = '';
12793         
12794         if (this.btn_text_show) {
12795             btn_text_select = 'Select';
12796             btn_text_done = 'Done';
12797             btn_text_cancel = 'Cancel'; 
12798         }
12799         
12800         var buttons = {
12801             tag : 'div',
12802             cls : 'tickable-buttons',
12803             cn : [
12804                 {
12805                     tag : 'button',
12806                     type : 'button',
12807                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12808                     //html : this.triggerText
12809                     html: btn_text_select
12810                 },
12811                 {
12812                     tag : 'button',
12813                     type : 'button',
12814                     name : 'ok',
12815                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12816                     //html : 'Done'
12817                     html: btn_text_done
12818                 },
12819                 {
12820                     tag : 'button',
12821                     type : 'button',
12822                     name : 'cancel',
12823                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12824                     //html : 'Cancel'
12825                     html: btn_text_cancel
12826                 }
12827             ]
12828         };
12829         
12830         if(this.editable){
12831             buttons.cn.unshift({
12832                 tag: 'input',
12833                 cls: 'roo-select2-search-field-input'
12834             });
12835         }
12836         
12837         var _this = this;
12838         
12839         Roo.each(buttons.cn, function(c){
12840             if (_this.size) {
12841                 c.cls += ' btn-' + _this.size;
12842             }
12843
12844             if (_this.disabled) {
12845                 c.disabled = true;
12846             }
12847         });
12848         
12849         var box = {
12850             tag: 'div',
12851             cn: [
12852                 {
12853                     tag: 'input',
12854                     type : 'hidden',
12855                     cls: 'form-hidden-field'
12856                 },
12857                 {
12858                     tag: 'ul',
12859                     cls: 'roo-select2-choices',
12860                     cn:[
12861                         {
12862                             tag: 'li',
12863                             cls: 'roo-select2-search-field',
12864                             cn: [
12865                                 buttons
12866                             ]
12867                         }
12868                     ]
12869                 }
12870             ]
12871         };
12872         
12873         var combobox = {
12874             cls: 'roo-select2-container input-group roo-select2-container-multi',
12875             cn: [
12876                 box
12877 //                {
12878 //                    tag: 'ul',
12879 //                    cls: 'typeahead typeahead-long dropdown-menu',
12880 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12881 //                }
12882             ]
12883         };
12884         
12885         if(this.hasFeedback && !this.allowBlank){
12886             
12887             var feedback = {
12888                 tag: 'span',
12889                 cls: 'glyphicon form-control-feedback'
12890             };
12891
12892             combobox.cn.push(feedback);
12893         }
12894         
12895         
12896         if (align ==='left' && this.fieldLabel.length) {
12897             
12898             cfg.cls += ' roo-form-group-label-left';
12899             
12900             cfg.cn = [
12901                 {
12902                     tag : 'i',
12903                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12904                     tooltip : 'This field is required'
12905                 },
12906                 {
12907                     tag: 'label',
12908                     'for' :  id,
12909                     cls : 'control-label',
12910                     html : this.fieldLabel
12911
12912                 },
12913                 {
12914                     cls : "", 
12915                     cn: [
12916                         combobox
12917                     ]
12918                 }
12919
12920             ];
12921             
12922             var labelCfg = cfg.cn[1];
12923             var contentCfg = cfg.cn[2];
12924             
12925
12926             if(this.indicatorpos == 'right'){
12927                 
12928                 cfg.cn = [
12929                     {
12930                         tag: 'label',
12931                         'for' :  id,
12932                         cls : 'control-label',
12933                         cn : [
12934                             {
12935                                 tag : 'span',
12936                                 html : this.fieldLabel
12937                             },
12938                             {
12939                                 tag : 'i',
12940                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12941                                 tooltip : 'This field is required'
12942                             }
12943                         ]
12944                     },
12945                     {
12946                         cls : "",
12947                         cn: [
12948                             combobox
12949                         ]
12950                     }
12951
12952                 ];
12953                 
12954                 
12955                 
12956                 labelCfg = cfg.cn[0];
12957                 contentCfg = cfg.cn[1];
12958             
12959             }
12960             
12961             if(this.labelWidth > 12){
12962                 labelCfg.style = "width: " + this.labelWidth + 'px';
12963             }
12964             
12965             if(this.labelWidth < 13 && this.labelmd == 0){
12966                 this.labelmd = this.labelWidth;
12967             }
12968             
12969             if(this.labellg > 0){
12970                 labelCfg.cls += ' col-lg-' + this.labellg;
12971                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12972             }
12973             
12974             if(this.labelmd > 0){
12975                 labelCfg.cls += ' col-md-' + this.labelmd;
12976                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12977             }
12978             
12979             if(this.labelsm > 0){
12980                 labelCfg.cls += ' col-sm-' + this.labelsm;
12981                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12982             }
12983             
12984             if(this.labelxs > 0){
12985                 labelCfg.cls += ' col-xs-' + this.labelxs;
12986                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12987             }
12988                 
12989                 
12990         } else if ( this.fieldLabel.length) {
12991 //                Roo.log(" label");
12992                  cfg.cn = [
12993                     {
12994                         tag : 'i',
12995                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12996                         tooltip : 'This field is required'
12997                     },
12998                     {
12999                         tag: 'label',
13000                         //cls : 'input-group-addon',
13001                         html : this.fieldLabel
13002                     },
13003                     combobox
13004                 ];
13005                 
13006                 if(this.indicatorpos == 'right'){
13007                     cfg.cn = [
13008                         {
13009                             tag: 'label',
13010                             //cls : 'input-group-addon',
13011                             html : this.fieldLabel
13012                         },
13013                         {
13014                             tag : 'i',
13015                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13016                             tooltip : 'This field is required'
13017                         },
13018                         combobox
13019                     ];
13020                     
13021                 }
13022
13023         } else {
13024             
13025 //                Roo.log(" no label && no align");
13026                 cfg = combobox
13027                      
13028                 
13029         }
13030          
13031         var settings=this;
13032         ['xs','sm','md','lg'].map(function(size){
13033             if (settings[size]) {
13034                 cfg.cls += ' col-' + size + '-' + settings[size];
13035             }
13036         });
13037         
13038         return cfg;
13039         
13040     },
13041     
13042     _initEventsCalled : false,
13043     
13044     // private
13045     initEvents: function()
13046     {   
13047         if (this._initEventsCalled) { // as we call render... prevent looping...
13048             return;
13049         }
13050         this._initEventsCalled = true;
13051         
13052         if (!this.store) {
13053             throw "can not find store for combo";
13054         }
13055         
13056         this.indicator = this.indicatorEl();
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.parent = this;
13060         
13061         // if we are building from html. then this element is so complex, that we can not really
13062         // use the rendered HTML.
13063         // so we have to trash and replace the previous code.
13064         if (Roo.XComponent.build_from_html) {
13065             // remove this element....
13066             var e = this.el.dom, k=0;
13067             while (e ) { e = e.previousSibling;  ++k;}
13068
13069             this.el.remove();
13070             
13071             this.el=false;
13072             this.rendered = false;
13073             
13074             this.render(this.parent().getChildContainer(true), k);
13075         }
13076         
13077         if(Roo.isIOS && this.useNativeIOS){
13078             this.initIOSView();
13079             return;
13080         }
13081         
13082         /*
13083          * Touch Devices
13084          */
13085         
13086         if(Roo.isTouch && this.mobileTouchView){
13087             this.initTouchView();
13088             return;
13089         }
13090         
13091         if(this.tickable){
13092             this.initTickableEvents();
13093             return;
13094         }
13095         
13096         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13097         
13098         if(this.hiddenName){
13099             
13100             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13101             
13102             this.hiddenField.dom.value =
13103                 this.hiddenValue !== undefined ? this.hiddenValue :
13104                 this.value !== undefined ? this.value : '';
13105
13106             // prevent input submission
13107             this.el.dom.removeAttribute('name');
13108             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13109              
13110              
13111         }
13112         //if(Roo.isGecko){
13113         //    this.el.dom.setAttribute('autocomplete', 'off');
13114         //}
13115         
13116         var cls = 'x-combo-list';
13117         
13118         //this.list = new Roo.Layer({
13119         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13120         //});
13121         
13122         var _this = this;
13123         
13124         (function(){
13125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13126             _this.list.setWidth(lw);
13127         }).defer(100);
13128         
13129         this.list.on('mouseover', this.onViewOver, this);
13130         this.list.on('mousemove', this.onViewMove, this);
13131         this.list.on('scroll', this.onViewScroll, this);
13132         
13133         /*
13134         this.list.swallowEvent('mousewheel');
13135         this.assetHeight = 0;
13136
13137         if(this.title){
13138             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13139             this.assetHeight += this.header.getHeight();
13140         }
13141
13142         this.innerList = this.list.createChild({cls:cls+'-inner'});
13143         this.innerList.on('mouseover', this.onViewOver, this);
13144         this.innerList.on('mousemove', this.onViewMove, this);
13145         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13146         
13147         if(this.allowBlank && !this.pageSize && !this.disableClear){
13148             this.footer = this.list.createChild({cls:cls+'-ft'});
13149             this.pageTb = new Roo.Toolbar(this.footer);
13150            
13151         }
13152         if(this.pageSize){
13153             this.footer = this.list.createChild({cls:cls+'-ft'});
13154             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13155                     {pageSize: this.pageSize});
13156             
13157         }
13158         
13159         if (this.pageTb && this.allowBlank && !this.disableClear) {
13160             var _this = this;
13161             this.pageTb.add(new Roo.Toolbar.Fill(), {
13162                 cls: 'x-btn-icon x-btn-clear',
13163                 text: '&#160;',
13164                 handler: function()
13165                 {
13166                     _this.collapse();
13167                     _this.clearValue();
13168                     _this.onSelect(false, -1);
13169                 }
13170             });
13171         }
13172         if (this.footer) {
13173             this.assetHeight += this.footer.getHeight();
13174         }
13175         */
13176             
13177         if(!this.tpl){
13178             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13179         }
13180
13181         this.view = new Roo.View(this.list, this.tpl, {
13182             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13183         });
13184         //this.view.wrapEl.setDisplayed(false);
13185         this.view.on('click', this.onViewClick, this);
13186         
13187         
13188         this.store.on('beforeload', this.onBeforeLoad, this);
13189         this.store.on('load', this.onLoad, this);
13190         this.store.on('loadexception', this.onLoadException, this);
13191         /*
13192         if(this.resizable){
13193             this.resizer = new Roo.Resizable(this.list,  {
13194                pinned:true, handles:'se'
13195             });
13196             this.resizer.on('resize', function(r, w, h){
13197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13198                 this.listWidth = w;
13199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13200                 this.restrictHeight();
13201             }, this);
13202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13203         }
13204         */
13205         if(!this.editable){
13206             this.editable = true;
13207             this.setEditable(false);
13208         }
13209         
13210         /*
13211         
13212         if (typeof(this.events.add.listeners) != 'undefined') {
13213             
13214             this.addicon = this.wrap.createChild(
13215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13216        
13217             this.addicon.on('click', function(e) {
13218                 this.fireEvent('add', this);
13219             }, this);
13220         }
13221         if (typeof(this.events.edit.listeners) != 'undefined') {
13222             
13223             this.editicon = this.wrap.createChild(
13224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13225             if (this.addicon) {
13226                 this.editicon.setStyle('margin-left', '40px');
13227             }
13228             this.editicon.on('click', function(e) {
13229                 
13230                 // we fire even  if inothing is selected..
13231                 this.fireEvent('edit', this, this.lastData );
13232                 
13233             }, this);
13234         }
13235         */
13236         
13237         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13238             "up" : function(e){
13239                 this.inKeyMode = true;
13240                 this.selectPrev();
13241             },
13242
13243             "down" : function(e){
13244                 if(!this.isExpanded()){
13245                     this.onTriggerClick();
13246                 }else{
13247                     this.inKeyMode = true;
13248                     this.selectNext();
13249                 }
13250             },
13251
13252             "enter" : function(e){
13253 //                this.onViewClick();
13254                 //return true;
13255                 this.collapse();
13256                 
13257                 if(this.fireEvent("specialkey", this, e)){
13258                     this.onViewClick(false);
13259                 }
13260                 
13261                 return true;
13262             },
13263
13264             "esc" : function(e){
13265                 this.collapse();
13266             },
13267
13268             "tab" : function(e){
13269                 this.collapse();
13270                 
13271                 if(this.fireEvent("specialkey", this, e)){
13272                     this.onViewClick(false);
13273                 }
13274                 
13275                 return true;
13276             },
13277
13278             scope : this,
13279
13280             doRelay : function(foo, bar, hname){
13281                 if(hname == 'down' || this.scope.isExpanded()){
13282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13283                 }
13284                 return true;
13285             },
13286
13287             forceKeyDown: true
13288         });
13289         
13290         
13291         this.queryDelay = Math.max(this.queryDelay || 10,
13292                 this.mode == 'local' ? 10 : 250);
13293         
13294         
13295         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13296         
13297         if(this.typeAhead){
13298             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13299         }
13300         if(this.editable !== false){
13301             this.inputEl().on("keyup", this.onKeyUp, this);
13302         }
13303         if(this.forceSelection){
13304             this.inputEl().on('blur', this.doForce, this);
13305         }
13306         
13307         if(this.multiple){
13308             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13309             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13310         }
13311     },
13312     
13313     initTickableEvents: function()
13314     {   
13315         this.createList();
13316         
13317         if(this.hiddenName){
13318             
13319             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13320             
13321             this.hiddenField.dom.value =
13322                 this.hiddenValue !== undefined ? this.hiddenValue :
13323                 this.value !== undefined ? this.value : '';
13324
13325             // prevent input submission
13326             this.el.dom.removeAttribute('name');
13327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328              
13329              
13330         }
13331         
13332 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13333         
13334         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13335         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13336         if(this.triggerList){
13337             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13338         }
13339          
13340         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13341         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13342         
13343         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13344         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13345         
13346         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13347         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13348         
13349         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13350         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13351         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13352         
13353         this.okBtn.hide();
13354         this.cancelBtn.hide();
13355         
13356         var _this = this;
13357         
13358         (function(){
13359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13360             _this.list.setWidth(lw);
13361         }).defer(100);
13362         
13363         this.list.on('mouseover', this.onViewOver, this);
13364         this.list.on('mousemove', this.onViewMove, this);
13365         
13366         this.list.on('scroll', this.onViewScroll, this);
13367         
13368         if(!this.tpl){
13369             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>';
13370         }
13371
13372         this.view = new Roo.View(this.list, this.tpl, {
13373             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13374         });
13375         
13376         //this.view.wrapEl.setDisplayed(false);
13377         this.view.on('click', this.onViewClick, this);
13378         
13379         
13380         
13381         this.store.on('beforeload', this.onBeforeLoad, this);
13382         this.store.on('load', this.onLoad, this);
13383         this.store.on('loadexception', this.onLoadException, this);
13384         
13385         if(this.editable){
13386             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13387                 "up" : function(e){
13388                     this.inKeyMode = true;
13389                     this.selectPrev();
13390                 },
13391
13392                 "down" : function(e){
13393                     this.inKeyMode = true;
13394                     this.selectNext();
13395                 },
13396
13397                 "enter" : function(e){
13398                     if(this.fireEvent("specialkey", this, e)){
13399                         this.onViewClick(false);
13400                     }
13401                     
13402                     return true;
13403                 },
13404
13405                 "esc" : function(e){
13406                     this.onTickableFooterButtonClick(e, false, false);
13407                 },
13408
13409                 "tab" : function(e){
13410                     this.fireEvent("specialkey", this, e);
13411                     
13412                     this.onTickableFooterButtonClick(e, false, false);
13413                     
13414                     return true;
13415                 },
13416
13417                 scope : this,
13418
13419                 doRelay : function(e, fn, key){
13420                     if(this.scope.isExpanded()){
13421                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13422                     }
13423                     return true;
13424                 },
13425
13426                 forceKeyDown: true
13427             });
13428         }
13429         
13430         this.queryDelay = Math.max(this.queryDelay || 10,
13431                 this.mode == 'local' ? 10 : 250);
13432         
13433         
13434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13435         
13436         if(this.typeAhead){
13437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13438         }
13439         
13440         if(this.editable !== false){
13441             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13442         }
13443         
13444         this.indicator = this.indicatorEl();
13445         
13446         if(this.indicator){
13447             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13448             this.indicator.hide();
13449         }
13450         
13451     },
13452
13453     onDestroy : function(){
13454         if(this.view){
13455             this.view.setStore(null);
13456             this.view.el.removeAllListeners();
13457             this.view.el.remove();
13458             this.view.purgeListeners();
13459         }
13460         if(this.list){
13461             this.list.dom.innerHTML  = '';
13462         }
13463         
13464         if(this.store){
13465             this.store.un('beforeload', this.onBeforeLoad, this);
13466             this.store.un('load', this.onLoad, this);
13467             this.store.un('loadexception', this.onLoadException, this);
13468         }
13469         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13470     },
13471
13472     // private
13473     fireKey : function(e){
13474         if(e.isNavKeyPress() && !this.list.isVisible()){
13475             this.fireEvent("specialkey", this, e);
13476         }
13477     },
13478
13479     // private
13480     onResize: function(w, h){
13481 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13482 //        
13483 //        if(typeof w != 'number'){
13484 //            // we do not handle it!?!?
13485 //            return;
13486 //        }
13487 //        var tw = this.trigger.getWidth();
13488 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13489 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13490 //        var x = w - tw;
13491 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13492 //            
13493 //        //this.trigger.setStyle('left', x+'px');
13494 //        
13495 //        if(this.list && this.listWidth === undefined){
13496 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13497 //            this.list.setWidth(lw);
13498 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13499 //        }
13500         
13501     
13502         
13503     },
13504
13505     /**
13506      * Allow or prevent the user from directly editing the field text.  If false is passed,
13507      * the user will only be able to select from the items defined in the dropdown list.  This method
13508      * is the runtime equivalent of setting the 'editable' config option at config time.
13509      * @param {Boolean} value True to allow the user to directly edit the field text
13510      */
13511     setEditable : function(value){
13512         if(value == this.editable){
13513             return;
13514         }
13515         this.editable = value;
13516         if(!value){
13517             this.inputEl().dom.setAttribute('readOnly', true);
13518             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13519             this.inputEl().addClass('x-combo-noedit');
13520         }else{
13521             this.inputEl().dom.setAttribute('readOnly', false);
13522             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13523             this.inputEl().removeClass('x-combo-noedit');
13524         }
13525     },
13526
13527     // private
13528     
13529     onBeforeLoad : function(combo,opts){
13530         if(!this.hasFocus){
13531             return;
13532         }
13533          if (!opts.add) {
13534             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13535          }
13536         this.restrictHeight();
13537         this.selectedIndex = -1;
13538     },
13539
13540     // private
13541     onLoad : function(){
13542         
13543         this.hasQuery = false;
13544         
13545         if(!this.hasFocus){
13546             return;
13547         }
13548         
13549         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13550             this.loading.hide();
13551         }
13552         
13553         if(this.store.getCount() > 0){
13554             
13555             this.expand();
13556             this.restrictHeight();
13557             if(this.lastQuery == this.allQuery){
13558                 if(this.editable && !this.tickable){
13559                     this.inputEl().dom.select();
13560                 }
13561                 
13562                 if(
13563                     !this.selectByValue(this.value, true) &&
13564                     this.autoFocus && 
13565                     (
13566                         !this.store.lastOptions ||
13567                         typeof(this.store.lastOptions.add) == 'undefined' || 
13568                         this.store.lastOptions.add != true
13569                     )
13570                 ){
13571                     this.select(0, true);
13572                 }
13573             }else{
13574                 if(this.autoFocus){
13575                     this.selectNext();
13576                 }
13577                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13578                     this.taTask.delay(this.typeAheadDelay);
13579                 }
13580             }
13581         }else{
13582             this.onEmptyResults();
13583         }
13584         
13585         //this.el.focus();
13586     },
13587     // private
13588     onLoadException : function()
13589     {
13590         this.hasQuery = false;
13591         
13592         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13593             this.loading.hide();
13594         }
13595         
13596         if(this.tickable && this.editable){
13597             return;
13598         }
13599         
13600         this.collapse();
13601         // only causes errors at present
13602         //Roo.log(this.store.reader.jsonData);
13603         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13604             // fixme
13605             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13606         //}
13607         
13608         
13609     },
13610     // private
13611     onTypeAhead : function(){
13612         if(this.store.getCount() > 0){
13613             var r = this.store.getAt(0);
13614             var newValue = r.data[this.displayField];
13615             var len = newValue.length;
13616             var selStart = this.getRawValue().length;
13617             
13618             if(selStart != len){
13619                 this.setRawValue(newValue);
13620                 this.selectText(selStart, newValue.length);
13621             }
13622         }
13623     },
13624
13625     // private
13626     onSelect : function(record, index){
13627         
13628         if(this.fireEvent('beforeselect', this, record, index) !== false){
13629         
13630             this.setFromData(index > -1 ? record.data : false);
13631             
13632             this.collapse();
13633             this.fireEvent('select', this, record, index);
13634         }
13635     },
13636
13637     /**
13638      * Returns the currently selected field value or empty string if no value is set.
13639      * @return {String} value The selected value
13640      */
13641     getValue : function()
13642     {
13643         if(Roo.isIOS && this.useNativeIOS){
13644             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13645         }
13646         
13647         if(this.multiple){
13648             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13649         }
13650         
13651         if(this.valueField){
13652             return typeof this.value != 'undefined' ? this.value : '';
13653         }else{
13654             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13655         }
13656     },
13657     
13658     getRawValue : function()
13659     {
13660         if(Roo.isIOS && this.useNativeIOS){
13661             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13662         }
13663         
13664         var v = this.inputEl().getValue();
13665         
13666         return v;
13667     },
13668
13669     /**
13670      * Clears any text/value currently set in the field
13671      */
13672     clearValue : function(){
13673         
13674         if(this.hiddenField){
13675             this.hiddenField.dom.value = '';
13676         }
13677         this.value = '';
13678         this.setRawValue('');
13679         this.lastSelectionText = '';
13680         this.lastData = false;
13681         
13682         var close = this.closeTriggerEl();
13683         
13684         if(close){
13685             close.hide();
13686         }
13687         
13688         this.validate();
13689         
13690     },
13691
13692     /**
13693      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13694      * will be displayed in the field.  If the value does not match the data value of an existing item,
13695      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13696      * Otherwise the field will be blank (although the value will still be set).
13697      * @param {String} value The value to match
13698      */
13699     setValue : function(v)
13700     {
13701         if(Roo.isIOS && this.useNativeIOS){
13702             this.setIOSValue(v);
13703             return;
13704         }
13705         
13706         if(this.multiple){
13707             this.syncValue();
13708             return;
13709         }
13710         
13711         var text = v;
13712         if(this.valueField){
13713             var r = this.findRecord(this.valueField, v);
13714             if(r){
13715                 text = r.data[this.displayField];
13716             }else if(this.valueNotFoundText !== undefined){
13717                 text = this.valueNotFoundText;
13718             }
13719         }
13720         this.lastSelectionText = text;
13721         if(this.hiddenField){
13722             this.hiddenField.dom.value = v;
13723         }
13724         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13725         this.value = v;
13726         
13727         var close = this.closeTriggerEl();
13728         
13729         if(close){
13730             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13731         }
13732         
13733         this.validate();
13734     },
13735     /**
13736      * @property {Object} the last set data for the element
13737      */
13738     
13739     lastData : false,
13740     /**
13741      * Sets the value of the field based on a object which is related to the record format for the store.
13742      * @param {Object} value the value to set as. or false on reset?
13743      */
13744     setFromData : function(o){
13745         
13746         if(this.multiple){
13747             this.addItem(o);
13748             return;
13749         }
13750             
13751         var dv = ''; // display value
13752         var vv = ''; // value value..
13753         this.lastData = o;
13754         if (this.displayField) {
13755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13756         } else {
13757             // this is an error condition!!!
13758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13759         }
13760         
13761         if(this.valueField){
13762             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13763         }
13764         
13765         var close = this.closeTriggerEl();
13766         
13767         if(close){
13768             if(dv.length || vv * 1 > 0){
13769                 close.show() ;
13770                 this.blockFocus=true;
13771             } else {
13772                 close.hide();
13773             }             
13774         }
13775         
13776         if(this.hiddenField){
13777             this.hiddenField.dom.value = vv;
13778             
13779             this.lastSelectionText = dv;
13780             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13781             this.value = vv;
13782             return;
13783         }
13784         // no hidden field.. - we store the value in 'value', but still display
13785         // display field!!!!
13786         this.lastSelectionText = dv;
13787         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13788         this.value = vv;
13789         
13790         
13791         
13792     },
13793     // private
13794     reset : function(){
13795         // overridden so that last data is reset..
13796         
13797         if(this.multiple){
13798             this.clearItem();
13799             return;
13800         }
13801         
13802         this.setValue(this.originalValue);
13803         //this.clearInvalid();
13804         this.lastData = false;
13805         if (this.view) {
13806             this.view.clearSelections();
13807         }
13808         
13809         this.validate();
13810     },
13811     // private
13812     findRecord : function(prop, value){
13813         var record;
13814         if(this.store.getCount() > 0){
13815             this.store.each(function(r){
13816                 if(r.data[prop] == value){
13817                     record = r;
13818                     return false;
13819                 }
13820                 return true;
13821             });
13822         }
13823         return record;
13824     },
13825     
13826     getName: function()
13827     {
13828         // returns hidden if it's set..
13829         if (!this.rendered) {return ''};
13830         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13831         
13832     },
13833     // private
13834     onViewMove : function(e, t){
13835         this.inKeyMode = false;
13836     },
13837
13838     // private
13839     onViewOver : function(e, t){
13840         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13841             return;
13842         }
13843         var item = this.view.findItemFromChild(t);
13844         
13845         if(item){
13846             var index = this.view.indexOf(item);
13847             this.select(index, false);
13848         }
13849     },
13850
13851     // private
13852     onViewClick : function(view, doFocus, el, e)
13853     {
13854         var index = this.view.getSelectedIndexes()[0];
13855         
13856         var r = this.store.getAt(index);
13857         
13858         if(this.tickable){
13859             
13860             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13861                 return;
13862             }
13863             
13864             var rm = false;
13865             var _this = this;
13866             
13867             Roo.each(this.tickItems, function(v,k){
13868                 
13869                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13870                     Roo.log(v);
13871                     _this.tickItems.splice(k, 1);
13872                     
13873                     if(typeof(e) == 'undefined' && view == false){
13874                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13875                     }
13876                     
13877                     rm = true;
13878                     return;
13879                 }
13880             });
13881             
13882             if(rm){
13883                 return;
13884             }
13885             
13886             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13887                 this.tickItems.push(r.data);
13888             }
13889             
13890             if(typeof(e) == 'undefined' && view == false){
13891                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13892             }
13893                     
13894             return;
13895         }
13896         
13897         if(r){
13898             this.onSelect(r, index);
13899         }
13900         if(doFocus !== false && !this.blockFocus){
13901             this.inputEl().focus();
13902         }
13903     },
13904
13905     // private
13906     restrictHeight : function(){
13907         //this.innerList.dom.style.height = '';
13908         //var inner = this.innerList.dom;
13909         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13910         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13911         //this.list.beginUpdate();
13912         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13913         this.list.alignTo(this.inputEl(), this.listAlign);
13914         this.list.alignTo(this.inputEl(), this.listAlign);
13915         //this.list.endUpdate();
13916     },
13917
13918     // private
13919     onEmptyResults : function(){
13920         
13921         if(this.tickable && this.editable){
13922             this.restrictHeight();
13923             return;
13924         }
13925         
13926         this.collapse();
13927     },
13928
13929     /**
13930      * Returns true if the dropdown list is expanded, else false.
13931      */
13932     isExpanded : function(){
13933         return this.list.isVisible();
13934     },
13935
13936     /**
13937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13939      * @param {String} value The data value of the item to select
13940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13941      * selected item if it is not currently in view (defaults to true)
13942      * @return {Boolean} True if the value matched an item in the list, else false
13943      */
13944     selectByValue : function(v, scrollIntoView){
13945         if(v !== undefined && v !== null){
13946             var r = this.findRecord(this.valueField || this.displayField, v);
13947             if(r){
13948                 this.select(this.store.indexOf(r), scrollIntoView);
13949                 return true;
13950             }
13951         }
13952         return false;
13953     },
13954
13955     /**
13956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13958      * @param {Number} index The zero-based index of the list item to select
13959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13960      * selected item if it is not currently in view (defaults to true)
13961      */
13962     select : function(index, scrollIntoView){
13963         this.selectedIndex = index;
13964         this.view.select(index);
13965         if(scrollIntoView !== false){
13966             var el = this.view.getNode(index);
13967             /*
13968              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13969              */
13970             if(el){
13971                 this.list.scrollChildIntoView(el, false);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectNext : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex < ct-1){
13983                 this.select(this.selectedIndex+1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     selectPrev : function(){
13990         var ct = this.store.getCount();
13991         if(ct > 0){
13992             if(this.selectedIndex == -1){
13993                 this.select(0);
13994             }else if(this.selectedIndex != 0){
13995                 this.select(this.selectedIndex-1);
13996             }
13997         }
13998     },
13999
14000     // private
14001     onKeyUp : function(e){
14002         if(this.editable !== false && !e.isSpecialKey()){
14003             this.lastKey = e.getKey();
14004             this.dqTask.delay(this.queryDelay);
14005         }
14006     },
14007
14008     // private
14009     validateBlur : function(){
14010         return !this.list || !this.list.isVisible();   
14011     },
14012
14013     // private
14014     initQuery : function(){
14015         
14016         var v = this.getRawValue();
14017         
14018         if(this.tickable && this.editable){
14019             v = this.tickableInputEl().getValue();
14020         }
14021         
14022         this.doQuery(v);
14023     },
14024
14025     // private
14026     doForce : function(){
14027         if(this.inputEl().dom.value.length > 0){
14028             this.inputEl().dom.value =
14029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14030              
14031         }
14032     },
14033
14034     /**
14035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14036      * query allowing the query action to be canceled if needed.
14037      * @param {String} query The SQL query to execute
14038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14040      * saved in the current store (defaults to false)
14041      */
14042     doQuery : function(q, forceAll){
14043         
14044         if(q === undefined || q === null){
14045             q = '';
14046         }
14047         var qe = {
14048             query: q,
14049             forceAll: forceAll,
14050             combo: this,
14051             cancel:false
14052         };
14053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14054             return false;
14055         }
14056         q = qe.query;
14057         
14058         forceAll = qe.forceAll;
14059         if(forceAll === true || (q.length >= this.minChars)){
14060             
14061             this.hasQuery = true;
14062             
14063             if(this.lastQuery != q || this.alwaysQuery){
14064                 this.lastQuery = q;
14065                 if(this.mode == 'local'){
14066                     Roo.log('it is local');
14067                     this.selectedIndex = -1;
14068                     if(forceAll){
14069                         this.store.clearFilter();
14070                     }else{
14071                         
14072                         if(this.specialFilter){
14073                             this.fireEvent('specialfilter', this);
14074                             this.onLoad();
14075                             return;
14076                         }
14077                         
14078                         this.store.filter(this.displayField, q);
14079                     }
14080                     
14081                     this.store.fireEvent("datachanged", this.store);
14082                     
14083                     this.onLoad();
14084                     
14085                 }else{
14086                     
14087                     this.store.baseParams[this.queryParam] = q;
14088                     
14089                     var options = {params : this.getParams(q)};
14090                     
14091                     if(this.loadNext){
14092                         options.add = true;
14093                         options.params.start = this.page * this.pageSize;
14094                     }
14095                     
14096                     this.store.load(options);
14097                     
14098                     /*
14099                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14100                      *  we should expand the list on onLoad
14101                      *  so command out it
14102                      */
14103 //                    this.expand();
14104                 }
14105             }else{
14106                 this.selectedIndex = -1;
14107                 this.onLoad();   
14108             }
14109         }
14110         
14111         this.loadNext = false;
14112     },
14113     
14114     // private
14115     getParams : function(q){
14116         var p = {};
14117         //p[this.queryParam] = q;
14118         
14119         if(this.pageSize){
14120             p.start = 0;
14121             p.limit = this.pageSize;
14122         }
14123         return p;
14124     },
14125
14126     /**
14127      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14128      */
14129     collapse : function(){
14130         if(!this.isExpanded()){
14131             return;
14132         }
14133         
14134         this.list.hide();
14135         
14136         this.hasFocus = false;
14137         
14138         if(this.tickable){
14139             this.okBtn.hide();
14140             this.cancelBtn.hide();
14141             this.trigger.show();
14142             
14143             if(this.editable){
14144                 this.tickableInputEl().dom.value = '';
14145                 this.tickableInputEl().blur();
14146             }
14147             
14148         }
14149         
14150         Roo.get(document).un('mousedown', this.collapseIf, this);
14151         Roo.get(document).un('mousewheel', this.collapseIf, this);
14152         if (!this.editable) {
14153             Roo.get(document).un('keydown', this.listKeyPress, this);
14154         }
14155         this.fireEvent('collapse', this);
14156         
14157         this.validate();
14158     },
14159
14160     // private
14161     collapseIf : function(e){
14162         var in_combo  = e.within(this.el);
14163         var in_list =  e.within(this.list);
14164         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14165         
14166         if (in_combo || in_list || is_list) {
14167             //e.stopPropagation();
14168             return;
14169         }
14170         
14171         if(this.tickable){
14172             this.onTickableFooterButtonClick(e, false, false);
14173         }
14174
14175         this.collapse();
14176         
14177     },
14178
14179     /**
14180      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14181      */
14182     expand : function(){
14183        
14184         if(this.isExpanded() || !this.hasFocus){
14185             return;
14186         }
14187         
14188         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14189         this.list.setWidth(lw);
14190         
14191         Roo.log('expand');
14192         
14193         this.list.show();
14194         
14195         this.restrictHeight();
14196         
14197         if(this.tickable){
14198             
14199             this.tickItems = Roo.apply([], this.item);
14200             
14201             this.okBtn.show();
14202             this.cancelBtn.show();
14203             this.trigger.hide();
14204             
14205             if(this.editable){
14206                 this.tickableInputEl().focus();
14207             }
14208             
14209         }
14210         
14211         Roo.get(document).on('mousedown', this.collapseIf, this);
14212         Roo.get(document).on('mousewheel', this.collapseIf, this);
14213         if (!this.editable) {
14214             Roo.get(document).on('keydown', this.listKeyPress, this);
14215         }
14216         
14217         this.fireEvent('expand', this);
14218     },
14219
14220     // private
14221     // Implements the default empty TriggerField.onTriggerClick function
14222     onTriggerClick : function(e)
14223     {
14224         Roo.log('trigger click');
14225         
14226         if(this.disabled || !this.triggerList){
14227             return;
14228         }
14229         
14230         this.page = 0;
14231         this.loadNext = false;
14232         
14233         if(this.isExpanded()){
14234             this.collapse();
14235             if (!this.blockFocus) {
14236                 this.inputEl().focus();
14237             }
14238             
14239         }else {
14240             this.hasFocus = true;
14241             if(this.triggerAction == 'all') {
14242                 this.doQuery(this.allQuery, true);
14243             } else {
14244                 this.doQuery(this.getRawValue());
14245             }
14246             if (!this.blockFocus) {
14247                 this.inputEl().focus();
14248             }
14249         }
14250     },
14251     
14252     onTickableTriggerClick : function(e)
14253     {
14254         if(this.disabled){
14255             return;
14256         }
14257         
14258         this.page = 0;
14259         this.loadNext = false;
14260         this.hasFocus = true;
14261         
14262         if(this.triggerAction == 'all') {
14263             this.doQuery(this.allQuery, true);
14264         } else {
14265             this.doQuery(this.getRawValue());
14266         }
14267     },
14268     
14269     onSearchFieldClick : function(e)
14270     {
14271         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14272             this.onTickableFooterButtonClick(e, false, false);
14273             return;
14274         }
14275         
14276         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14277             return;
14278         }
14279         
14280         this.page = 0;
14281         this.loadNext = false;
14282         this.hasFocus = true;
14283         
14284         if(this.triggerAction == 'all') {
14285             this.doQuery(this.allQuery, true);
14286         } else {
14287             this.doQuery(this.getRawValue());
14288         }
14289     },
14290     
14291     listKeyPress : function(e)
14292     {
14293         //Roo.log('listkeypress');
14294         // scroll to first matching element based on key pres..
14295         if (e.isSpecialKey()) {
14296             return false;
14297         }
14298         var k = String.fromCharCode(e.getKey()).toUpperCase();
14299         //Roo.log(k);
14300         var match  = false;
14301         var csel = this.view.getSelectedNodes();
14302         var cselitem = false;
14303         if (csel.length) {
14304             var ix = this.view.indexOf(csel[0]);
14305             cselitem  = this.store.getAt(ix);
14306             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14307                 cselitem = false;
14308             }
14309             
14310         }
14311         
14312         this.store.each(function(v) { 
14313             if (cselitem) {
14314                 // start at existing selection.
14315                 if (cselitem.id == v.id) {
14316                     cselitem = false;
14317                 }
14318                 return true;
14319             }
14320                 
14321             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14322                 match = this.store.indexOf(v);
14323                 return false;
14324             }
14325             return true;
14326         }, this);
14327         
14328         if (match === false) {
14329             return true; // no more action?
14330         }
14331         // scroll to?
14332         this.view.select(match);
14333         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14334         sn.scrollIntoView(sn.dom.parentNode, false);
14335     },
14336     
14337     onViewScroll : function(e, t){
14338         
14339         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){
14340             return;
14341         }
14342         
14343         this.hasQuery = true;
14344         
14345         this.loading = this.list.select('.loading', true).first();
14346         
14347         if(this.loading === null){
14348             this.list.createChild({
14349                 tag: 'div',
14350                 cls: 'loading roo-select2-more-results roo-select2-active',
14351                 html: 'Loading more results...'
14352             });
14353             
14354             this.loading = this.list.select('.loading', true).first();
14355             
14356             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14357             
14358             this.loading.hide();
14359         }
14360         
14361         this.loading.show();
14362         
14363         var _combo = this;
14364         
14365         this.page++;
14366         this.loadNext = true;
14367         
14368         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14369         
14370         return;
14371     },
14372     
14373     addItem : function(o)
14374     {   
14375         var dv = ''; // display value
14376         
14377         if (this.displayField) {
14378             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14379         } else {
14380             // this is an error condition!!!
14381             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14382         }
14383         
14384         if(!dv.length){
14385             return;
14386         }
14387         
14388         var choice = this.choices.createChild({
14389             tag: 'li',
14390             cls: 'roo-select2-search-choice',
14391             cn: [
14392                 {
14393                     tag: 'div',
14394                     html: dv
14395                 },
14396                 {
14397                     tag: 'a',
14398                     href: '#',
14399                     cls: 'roo-select2-search-choice-close fa fa-times',
14400                     tabindex: '-1'
14401                 }
14402             ]
14403             
14404         }, this.searchField);
14405         
14406         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14407         
14408         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14409         
14410         this.item.push(o);
14411         
14412         this.lastData = o;
14413         
14414         this.syncValue();
14415         
14416         this.inputEl().dom.value = '';
14417         
14418         this.validate();
14419     },
14420     
14421     onRemoveItem : function(e, _self, o)
14422     {
14423         e.preventDefault();
14424         
14425         this.lastItem = Roo.apply([], this.item);
14426         
14427         var index = this.item.indexOf(o.data) * 1;
14428         
14429         if( index < 0){
14430             Roo.log('not this item?!');
14431             return;
14432         }
14433         
14434         this.item.splice(index, 1);
14435         o.item.remove();
14436         
14437         this.syncValue();
14438         
14439         this.fireEvent('remove', this, e);
14440         
14441         this.validate();
14442         
14443     },
14444     
14445     syncValue : function()
14446     {
14447         if(!this.item.length){
14448             this.clearValue();
14449             return;
14450         }
14451             
14452         var value = [];
14453         var _this = this;
14454         Roo.each(this.item, function(i){
14455             if(_this.valueField){
14456                 value.push(i[_this.valueField]);
14457                 return;
14458             }
14459
14460             value.push(i);
14461         });
14462
14463         this.value = value.join(',');
14464
14465         if(this.hiddenField){
14466             this.hiddenField.dom.value = this.value;
14467         }
14468         
14469         this.store.fireEvent("datachanged", this.store);
14470         
14471         this.validate();
14472     },
14473     
14474     clearItem : function()
14475     {
14476         if(!this.multiple){
14477             return;
14478         }
14479         
14480         this.item = [];
14481         
14482         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14483            c.remove();
14484         });
14485         
14486         this.syncValue();
14487         
14488         this.validate();
14489         
14490         if(this.tickable && !Roo.isTouch){
14491             this.view.refresh();
14492         }
14493     },
14494     
14495     inputEl: function ()
14496     {
14497         if(Roo.isIOS && this.useNativeIOS){
14498             return this.el.select('select.roo-ios-select', true).first();
14499         }
14500         
14501         if(Roo.isTouch && this.mobileTouchView){
14502             return this.el.select('input.form-control',true).first();
14503         }
14504         
14505         if(this.tickable){
14506             return this.searchField;
14507         }
14508         
14509         return this.el.select('input.form-control',true).first();
14510     },
14511     
14512     onTickableFooterButtonClick : function(e, btn, el)
14513     {
14514         e.preventDefault();
14515         
14516         this.lastItem = Roo.apply([], this.item);
14517         
14518         if(btn && btn.name == 'cancel'){
14519             this.tickItems = Roo.apply([], this.item);
14520             this.collapse();
14521             return;
14522         }
14523         
14524         this.clearItem();
14525         
14526         var _this = this;
14527         
14528         Roo.each(this.tickItems, function(o){
14529             _this.addItem(o);
14530         });
14531         
14532         this.collapse();
14533         
14534     },
14535     
14536     validate : function()
14537     {
14538         var v = this.getRawValue();
14539         
14540         if(this.multiple){
14541             v = this.getValue();
14542         }
14543         
14544         if(this.disabled || this.allowBlank || v.length){
14545             this.markValid();
14546             return true;
14547         }
14548         
14549         this.markInvalid();
14550         return false;
14551     },
14552     
14553     tickableInputEl : function()
14554     {
14555         if(!this.tickable || !this.editable){
14556             return this.inputEl();
14557         }
14558         
14559         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14560     },
14561     
14562     
14563     getAutoCreateTouchView : function()
14564     {
14565         var id = Roo.id();
14566         
14567         var cfg = {
14568             cls: 'form-group' //input-group
14569         };
14570         
14571         var input =  {
14572             tag: 'input',
14573             id : id,
14574             type : this.inputType,
14575             cls : 'form-control x-combo-noedit',
14576             autocomplete: 'new-password',
14577             placeholder : this.placeholder || '',
14578             readonly : true
14579         };
14580         
14581         if (this.name) {
14582             input.name = this.name;
14583         }
14584         
14585         if (this.size) {
14586             input.cls += ' input-' + this.size;
14587         }
14588         
14589         if (this.disabled) {
14590             input.disabled = true;
14591         }
14592         
14593         var inputblock = {
14594             cls : '',
14595             cn : [
14596                 input
14597             ]
14598         };
14599         
14600         if(this.before){
14601             inputblock.cls += ' input-group';
14602             
14603             inputblock.cn.unshift({
14604                 tag :'span',
14605                 cls : 'input-group-addon',
14606                 html : this.before
14607             });
14608         }
14609         
14610         if(this.removable && !this.multiple){
14611             inputblock.cls += ' roo-removable';
14612             
14613             inputblock.cn.push({
14614                 tag: 'button',
14615                 html : 'x',
14616                 cls : 'roo-combo-removable-btn close'
14617             });
14618         }
14619
14620         if(this.hasFeedback && !this.allowBlank){
14621             
14622             inputblock.cls += ' has-feedback';
14623             
14624             inputblock.cn.push({
14625                 tag: 'span',
14626                 cls: 'glyphicon form-control-feedback'
14627             });
14628             
14629         }
14630         
14631         if (this.after) {
14632             
14633             inputblock.cls += (this.before) ? '' : ' input-group';
14634             
14635             inputblock.cn.push({
14636                 tag :'span',
14637                 cls : 'input-group-addon',
14638                 html : this.after
14639             });
14640         }
14641
14642         var box = {
14643             tag: 'div',
14644             cn: [
14645                 {
14646                     tag: 'input',
14647                     type : 'hidden',
14648                     cls: 'form-hidden-field'
14649                 },
14650                 inputblock
14651             ]
14652             
14653         };
14654         
14655         if(this.multiple){
14656             box = {
14657                 tag: 'div',
14658                 cn: [
14659                     {
14660                         tag: 'input',
14661                         type : 'hidden',
14662                         cls: 'form-hidden-field'
14663                     },
14664                     {
14665                         tag: 'ul',
14666                         cls: 'roo-select2-choices',
14667                         cn:[
14668                             {
14669                                 tag: 'li',
14670                                 cls: 'roo-select2-search-field',
14671                                 cn: [
14672
14673                                     inputblock
14674                                 ]
14675                             }
14676                         ]
14677                     }
14678                 ]
14679             }
14680         };
14681         
14682         var combobox = {
14683             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14684             cn: [
14685                 box
14686             ]
14687         };
14688         
14689         if(!this.multiple && this.showToggleBtn){
14690             
14691             var caret = {
14692                         tag: 'span',
14693                         cls: 'caret'
14694             };
14695             
14696             if (this.caret != false) {
14697                 caret = {
14698                      tag: 'i',
14699                      cls: 'fa fa-' + this.caret
14700                 };
14701                 
14702             }
14703             
14704             combobox.cn.push({
14705                 tag :'span',
14706                 cls : 'input-group-addon btn dropdown-toggle',
14707                 cn : [
14708                     caret,
14709                     {
14710                         tag: 'span',
14711                         cls: 'combobox-clear',
14712                         cn  : [
14713                             {
14714                                 tag : 'i',
14715                                 cls: 'icon-remove'
14716                             }
14717                         ]
14718                     }
14719                 ]
14720
14721             })
14722         }
14723         
14724         if(this.multiple){
14725             combobox.cls += ' roo-select2-container-multi';
14726         }
14727         
14728         var align = this.labelAlign || this.parentLabelAlign();
14729         
14730         if (align ==='left' && this.fieldLabel.length) {
14731
14732             cfg.cn = [
14733                 {
14734                    tag : 'i',
14735                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14736                    tooltip : 'This field is required'
14737                 },
14738                 {
14739                     tag: 'label',
14740                     cls : 'control-label',
14741                     html : this.fieldLabel
14742
14743                 },
14744                 {
14745                     cls : '', 
14746                     cn: [
14747                         combobox
14748                     ]
14749                 }
14750             ];
14751             
14752             var labelCfg = cfg.cn[1];
14753             var contentCfg = cfg.cn[2];
14754             
14755
14756             if(this.indicatorpos == 'right'){
14757                 cfg.cn = [
14758                     {
14759                         tag: 'label',
14760                         'for' :  id,
14761                         cls : 'control-label',
14762                         cn : [
14763                             {
14764                                 tag : 'span',
14765                                 html : this.fieldLabel
14766                             },
14767                             {
14768                                 tag : 'i',
14769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14770                                 tooltip : 'This field is required'
14771                             }
14772                         ]
14773                     },
14774                     {
14775                         cls : "",
14776                         cn: [
14777                             combobox
14778                         ]
14779                     }
14780
14781                 ];
14782                 
14783                 labelCfg = cfg.cn[0];
14784                 contentCfg = cfg.cn[1];
14785             }
14786             
14787            
14788             
14789             if(this.labelWidth > 12){
14790                 labelCfg.style = "width: " + this.labelWidth + 'px';
14791             }
14792             
14793             if(this.labelWidth < 13 && this.labelmd == 0){
14794                 this.labelmd = this.labelWidth;
14795             }
14796             
14797             if(this.labellg > 0){
14798                 labelCfg.cls += ' col-lg-' + this.labellg;
14799                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14800             }
14801             
14802             if(this.labelmd > 0){
14803                 labelCfg.cls += ' col-md-' + this.labelmd;
14804                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14805             }
14806             
14807             if(this.labelsm > 0){
14808                 labelCfg.cls += ' col-sm-' + this.labelsm;
14809                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14810             }
14811             
14812             if(this.labelxs > 0){
14813                 labelCfg.cls += ' col-xs-' + this.labelxs;
14814                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14815             }
14816                 
14817                 
14818         } else if ( this.fieldLabel.length) {
14819             cfg.cn = [
14820                 {
14821                    tag : 'i',
14822                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14823                    tooltip : 'This field is required'
14824                 },
14825                 {
14826                     tag: 'label',
14827                     cls : 'control-label',
14828                     html : this.fieldLabel
14829
14830                 },
14831                 {
14832                     cls : '', 
14833                     cn: [
14834                         combobox
14835                     ]
14836                 }
14837             ];
14838             
14839             if(this.indicatorpos == 'right'){
14840                 cfg.cn = [
14841                     {
14842                         tag: 'label',
14843                         cls : 'control-label',
14844                         html : this.fieldLabel,
14845                         cn : [
14846                             {
14847                                tag : 'i',
14848                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14849                                tooltip : 'This field is required'
14850                             }
14851                         ]
14852                     },
14853                     {
14854                         cls : '', 
14855                         cn: [
14856                             combobox
14857                         ]
14858                     }
14859                 ];
14860             }
14861         } else {
14862             cfg.cn = combobox;    
14863         }
14864         
14865         
14866         var settings = this;
14867         
14868         ['xs','sm','md','lg'].map(function(size){
14869             if (settings[size]) {
14870                 cfg.cls += ' col-' + size + '-' + settings[size];
14871             }
14872         });
14873         
14874         return cfg;
14875     },
14876     
14877     initTouchView : function()
14878     {
14879         this.renderTouchView();
14880         
14881         this.touchViewEl.on('scroll', function(){
14882             this.el.dom.scrollTop = 0;
14883         }, this);
14884         
14885         this.originalValue = this.getValue();
14886         
14887         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14888         
14889         this.inputEl().on("click", this.showTouchView, this);
14890         if (this.triggerEl) {
14891             this.triggerEl.on("click", this.showTouchView, this);
14892         }
14893         
14894         
14895         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14896         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14897         
14898         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14899         
14900         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14901         this.store.on('load', this.onTouchViewLoad, this);
14902         this.store.on('loadexception', this.onTouchViewLoadException, this);
14903         
14904         if(this.hiddenName){
14905             
14906             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14907             
14908             this.hiddenField.dom.value =
14909                 this.hiddenValue !== undefined ? this.hiddenValue :
14910                 this.value !== undefined ? this.value : '';
14911         
14912             this.el.dom.removeAttribute('name');
14913             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14914         }
14915         
14916         if(this.multiple){
14917             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14918             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14919         }
14920         
14921         if(this.removable && !this.multiple){
14922             var close = this.closeTriggerEl();
14923             if(close){
14924                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14925                 close.on('click', this.removeBtnClick, this, close);
14926             }
14927         }
14928         /*
14929          * fix the bug in Safari iOS8
14930          */
14931         this.inputEl().on("focus", function(e){
14932             document.activeElement.blur();
14933         }, this);
14934         
14935         return;
14936         
14937         
14938     },
14939     
14940     renderTouchView : function()
14941     {
14942         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14943         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14944         
14945         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14946         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14947         
14948         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14949         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14950         this.touchViewBodyEl.setStyle('overflow', 'auto');
14951         
14952         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14953         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14954         
14955         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14956         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14957         
14958     },
14959     
14960     showTouchView : function()
14961     {
14962         if(this.disabled){
14963             return;
14964         }
14965         
14966         this.touchViewHeaderEl.hide();
14967
14968         if(this.modalTitle.length){
14969             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14970             this.touchViewHeaderEl.show();
14971         }
14972
14973         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14974         this.touchViewEl.show();
14975
14976         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14977         
14978         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14979         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14980
14981         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14982
14983         if(this.modalTitle.length){
14984             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14985         }
14986         
14987         this.touchViewBodyEl.setHeight(bodyHeight);
14988
14989         if(this.animate){
14990             var _this = this;
14991             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14992         }else{
14993             this.touchViewEl.addClass('in');
14994         }
14995
14996         this.doTouchViewQuery();
14997         
14998     },
14999     
15000     hideTouchView : function()
15001     {
15002         this.touchViewEl.removeClass('in');
15003
15004         if(this.animate){
15005             var _this = this;
15006             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15007         }else{
15008             this.touchViewEl.setStyle('display', 'none');
15009         }
15010         
15011     },
15012     
15013     setTouchViewValue : function()
15014     {
15015         if(this.multiple){
15016             this.clearItem();
15017         
15018             var _this = this;
15019
15020             Roo.each(this.tickItems, function(o){
15021                 this.addItem(o);
15022             }, this);
15023         }
15024         
15025         this.hideTouchView();
15026     },
15027     
15028     doTouchViewQuery : function()
15029     {
15030         var qe = {
15031             query: '',
15032             forceAll: true,
15033             combo: this,
15034             cancel:false
15035         };
15036         
15037         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15038             return false;
15039         }
15040         
15041         if(!this.alwaysQuery || this.mode == 'local'){
15042             this.onTouchViewLoad();
15043             return;
15044         }
15045         
15046         this.store.load();
15047     },
15048     
15049     onTouchViewBeforeLoad : function(combo,opts)
15050     {
15051         return;
15052     },
15053
15054     // private
15055     onTouchViewLoad : function()
15056     {
15057         if(this.store.getCount() < 1){
15058             this.onTouchViewEmptyResults();
15059             return;
15060         }
15061         
15062         this.clearTouchView();
15063         
15064         var rawValue = this.getRawValue();
15065         
15066         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15067         
15068         this.tickItems = [];
15069         
15070         this.store.data.each(function(d, rowIndex){
15071             var row = this.touchViewListGroup.createChild(template);
15072             
15073             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15074                 row.addClass(d.data.cls);
15075             }
15076             
15077             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15078                 var cfg = {
15079                     data : d.data,
15080                     html : d.data[this.displayField]
15081                 };
15082                 
15083                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15084                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15085                 }
15086             }
15087             row.removeClass('selected');
15088             if(!this.multiple && this.valueField &&
15089                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15090             {
15091                 // radio buttons..
15092                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15093                 row.addClass('selected');
15094             }
15095             
15096             if(this.multiple && this.valueField &&
15097                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15098             {
15099                 
15100                 // checkboxes...
15101                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15102                 this.tickItems.push(d.data);
15103             }
15104             
15105             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15106             
15107         }, this);
15108         
15109         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15110         
15111         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15112
15113         if(this.modalTitle.length){
15114             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15115         }
15116
15117         var listHeight = this.touchViewListGroup.getHeight();
15118         
15119         var _this = this;
15120         
15121         if(firstChecked && listHeight > bodyHeight){
15122             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15123         }
15124         
15125     },
15126     
15127     onTouchViewLoadException : function()
15128     {
15129         this.hideTouchView();
15130     },
15131     
15132     onTouchViewEmptyResults : function()
15133     {
15134         this.clearTouchView();
15135         
15136         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15137         
15138         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15139         
15140     },
15141     
15142     clearTouchView : function()
15143     {
15144         this.touchViewListGroup.dom.innerHTML = '';
15145     },
15146     
15147     onTouchViewClick : function(e, el, o)
15148     {
15149         e.preventDefault();
15150         
15151         var row = o.row;
15152         var rowIndex = o.rowIndex;
15153         
15154         var r = this.store.getAt(rowIndex);
15155         
15156         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15157             
15158             if(!this.multiple){
15159                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15160                     c.dom.removeAttribute('checked');
15161                 }, this);
15162
15163                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15164
15165                 this.setFromData(r.data);
15166
15167                 var close = this.closeTriggerEl();
15168
15169                 if(close){
15170                     close.show();
15171                 }
15172
15173                 this.hideTouchView();
15174
15175                 this.fireEvent('select', this, r, rowIndex);
15176
15177                 return;
15178             }
15179
15180             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15181                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15182                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15183                 return;
15184             }
15185
15186             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15187             this.addItem(r.data);
15188             this.tickItems.push(r.data);
15189         }
15190     },
15191     
15192     getAutoCreateNativeIOS : function()
15193     {
15194         var cfg = {
15195             cls: 'form-group' //input-group,
15196         };
15197         
15198         var combobox =  {
15199             tag: 'select',
15200             cls : 'roo-ios-select'
15201         };
15202         
15203         if (this.name) {
15204             combobox.name = this.name;
15205         }
15206         
15207         if (this.disabled) {
15208             combobox.disabled = true;
15209         }
15210         
15211         var settings = this;
15212         
15213         ['xs','sm','md','lg'].map(function(size){
15214             if (settings[size]) {
15215                 cfg.cls += ' col-' + size + '-' + settings[size];
15216             }
15217         });
15218         
15219         cfg.cn = combobox;
15220         
15221         return cfg;
15222         
15223     },
15224     
15225     initIOSView : function()
15226     {
15227         this.store.on('load', this.onIOSViewLoad, this);
15228         
15229         return;
15230     },
15231     
15232     onIOSViewLoad : function()
15233     {
15234         if(this.store.getCount() < 1){
15235             return;
15236         }
15237         
15238         this.clearIOSView();
15239         
15240         if(this.allowBlank) {
15241             
15242             var default_text = '-- SELECT --';
15243             
15244             if(this.placeholder.length){
15245                 default_text = this.placeholder;
15246             }
15247             
15248             if(this.emptyTitle.length){
15249                 default_text += ' - ' + this.emptyTitle + ' -';
15250             }
15251             
15252             var opt = this.inputEl().createChild({
15253                 tag: 'option',
15254                 value : 0,
15255                 html : default_text
15256             });
15257             
15258             var o = {};
15259             o[this.valueField] = 0;
15260             o[this.displayField] = default_text;
15261             
15262             this.ios_options.push({
15263                 data : o,
15264                 el : opt
15265             });
15266             
15267         }
15268         
15269         this.store.data.each(function(d, rowIndex){
15270             
15271             var html = '';
15272             
15273             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15274                 html = d.data[this.displayField];
15275             }
15276             
15277             var value = '';
15278             
15279             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15280                 value = d.data[this.valueField];
15281             }
15282             
15283             var option = {
15284                 tag: 'option',
15285                 value : value,
15286                 html : html
15287             };
15288             
15289             if(this.value == d.data[this.valueField]){
15290                 option['selected'] = true;
15291             }
15292             
15293             var opt = this.inputEl().createChild(option);
15294             
15295             this.ios_options.push({
15296                 data : d.data,
15297                 el : opt
15298             });
15299             
15300         }, this);
15301         
15302         this.inputEl().on('change', function(){
15303            this.fireEvent('select', this);
15304         }, this);
15305         
15306     },
15307     
15308     clearIOSView: function()
15309     {
15310         this.inputEl().dom.innerHTML = '';
15311         
15312         this.ios_options = [];
15313     },
15314     
15315     setIOSValue: function(v)
15316     {
15317         this.value = v;
15318         
15319         if(!this.ios_options){
15320             return;
15321         }
15322         
15323         Roo.each(this.ios_options, function(opts){
15324            
15325            opts.el.dom.removeAttribute('selected');
15326            
15327            if(opts.data[this.valueField] != v){
15328                return;
15329            }
15330            
15331            opts.el.dom.setAttribute('selected', true);
15332            
15333         }, this);
15334     }
15335
15336     /** 
15337     * @cfg {Boolean} grow 
15338     * @hide 
15339     */
15340     /** 
15341     * @cfg {Number} growMin 
15342     * @hide 
15343     */
15344     /** 
15345     * @cfg {Number} growMax 
15346     * @hide 
15347     */
15348     /**
15349      * @hide
15350      * @method autoSize
15351      */
15352 });
15353
15354 Roo.apply(Roo.bootstrap.ComboBox,  {
15355     
15356     header : {
15357         tag: 'div',
15358         cls: 'modal-header',
15359         cn: [
15360             {
15361                 tag: 'h4',
15362                 cls: 'modal-title'
15363             }
15364         ]
15365     },
15366     
15367     body : {
15368         tag: 'div',
15369         cls: 'modal-body',
15370         cn: [
15371             {
15372                 tag: 'ul',
15373                 cls: 'list-group'
15374             }
15375         ]
15376     },
15377     
15378     listItemRadio : {
15379         tag: 'li',
15380         cls: 'list-group-item',
15381         cn: [
15382             {
15383                 tag: 'span',
15384                 cls: 'roo-combobox-list-group-item-value'
15385             },
15386             {
15387                 tag: 'div',
15388                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15389                 cn: [
15390                     {
15391                         tag: 'input',
15392                         type: 'radio'
15393                     },
15394                     {
15395                         tag: 'label'
15396                     }
15397                 ]
15398             }
15399         ]
15400     },
15401     
15402     listItemCheckbox : {
15403         tag: 'li',
15404         cls: 'list-group-item',
15405         cn: [
15406             {
15407                 tag: 'span',
15408                 cls: 'roo-combobox-list-group-item-value'
15409             },
15410             {
15411                 tag: 'div',
15412                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15413                 cn: [
15414                     {
15415                         tag: 'input',
15416                         type: 'checkbox'
15417                     },
15418                     {
15419                         tag: 'label'
15420                     }
15421                 ]
15422             }
15423         ]
15424     },
15425     
15426     emptyResult : {
15427         tag: 'div',
15428         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15429     },
15430     
15431     footer : {
15432         tag: 'div',
15433         cls: 'modal-footer',
15434         cn: [
15435             {
15436                 tag: 'div',
15437                 cls: 'row',
15438                 cn: [
15439                     {
15440                         tag: 'div',
15441                         cls: 'col-xs-6 text-left',
15442                         cn: {
15443                             tag: 'button',
15444                             cls: 'btn btn-danger roo-touch-view-cancel',
15445                             html: 'Cancel'
15446                         }
15447                     },
15448                     {
15449                         tag: 'div',
15450                         cls: 'col-xs-6 text-right',
15451                         cn: {
15452                             tag: 'button',
15453                             cls: 'btn btn-success roo-touch-view-ok',
15454                             html: 'OK'
15455                         }
15456                     }
15457                 ]
15458             }
15459         ]
15460         
15461     }
15462 });
15463
15464 Roo.apply(Roo.bootstrap.ComboBox,  {
15465     
15466     touchViewTemplate : {
15467         tag: 'div',
15468         cls: 'modal fade roo-combobox-touch-view',
15469         cn: [
15470             {
15471                 tag: 'div',
15472                 cls: 'modal-dialog',
15473                 style : 'position:fixed', // we have to fix position....
15474                 cn: [
15475                     {
15476                         tag: 'div',
15477                         cls: 'modal-content',
15478                         cn: [
15479                             Roo.bootstrap.ComboBox.header,
15480                             Roo.bootstrap.ComboBox.body,
15481                             Roo.bootstrap.ComboBox.footer
15482                         ]
15483                     }
15484                 ]
15485             }
15486         ]
15487     }
15488 });/*
15489  * Based on:
15490  * Ext JS Library 1.1.1
15491  * Copyright(c) 2006-2007, Ext JS, LLC.
15492  *
15493  * Originally Released Under LGPL - original licence link has changed is not relivant.
15494  *
15495  * Fork - LGPL
15496  * <script type="text/javascript">
15497  */
15498
15499 /**
15500  * @class Roo.View
15501  * @extends Roo.util.Observable
15502  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15503  * This class also supports single and multi selection modes. <br>
15504  * Create a data model bound view:
15505  <pre><code>
15506  var store = new Roo.data.Store(...);
15507
15508  var view = new Roo.View({
15509     el : "my-element",
15510     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15511  
15512     singleSelect: true,
15513     selectedClass: "ydataview-selected",
15514     store: store
15515  });
15516
15517  // listen for node click?
15518  view.on("click", function(vw, index, node, e){
15519  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15520  });
15521
15522  // load XML data
15523  dataModel.load("foobar.xml");
15524  </code></pre>
15525  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15526  * <br><br>
15527  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15528  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15529  * 
15530  * Note: old style constructor is still suported (container, template, config)
15531  * 
15532  * @constructor
15533  * Create a new View
15534  * @param {Object} config The config object
15535  * 
15536  */
15537 Roo.View = function(config, depreciated_tpl, depreciated_config){
15538     
15539     this.parent = false;
15540     
15541     if (typeof(depreciated_tpl) == 'undefined') {
15542         // new way.. - universal constructor.
15543         Roo.apply(this, config);
15544         this.el  = Roo.get(this.el);
15545     } else {
15546         // old format..
15547         this.el  = Roo.get(config);
15548         this.tpl = depreciated_tpl;
15549         Roo.apply(this, depreciated_config);
15550     }
15551     this.wrapEl  = this.el.wrap().wrap();
15552     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15553     
15554     
15555     if(typeof(this.tpl) == "string"){
15556         this.tpl = new Roo.Template(this.tpl);
15557     } else {
15558         // support xtype ctors..
15559         this.tpl = new Roo.factory(this.tpl, Roo);
15560     }
15561     
15562     
15563     this.tpl.compile();
15564     
15565     /** @private */
15566     this.addEvents({
15567         /**
15568          * @event beforeclick
15569          * Fires before a click is processed. Returns false to cancel the default action.
15570          * @param {Roo.View} this
15571          * @param {Number} index The index of the target node
15572          * @param {HTMLElement} node The target node
15573          * @param {Roo.EventObject} e The raw event object
15574          */
15575             "beforeclick" : true,
15576         /**
15577          * @event click
15578          * Fires when a template node is clicked.
15579          * @param {Roo.View} this
15580          * @param {Number} index The index of the target node
15581          * @param {HTMLElement} node The target node
15582          * @param {Roo.EventObject} e The raw event object
15583          */
15584             "click" : true,
15585         /**
15586          * @event dblclick
15587          * Fires when a template node is double clicked.
15588          * @param {Roo.View} this
15589          * @param {Number} index The index of the target node
15590          * @param {HTMLElement} node The target node
15591          * @param {Roo.EventObject} e The raw event object
15592          */
15593             "dblclick" : true,
15594         /**
15595          * @event contextmenu
15596          * Fires when a template node is right clicked.
15597          * @param {Roo.View} this
15598          * @param {Number} index The index of the target node
15599          * @param {HTMLElement} node The target node
15600          * @param {Roo.EventObject} e The raw event object
15601          */
15602             "contextmenu" : true,
15603         /**
15604          * @event selectionchange
15605          * Fires when the selected nodes change.
15606          * @param {Roo.View} this
15607          * @param {Array} selections Array of the selected nodes
15608          */
15609             "selectionchange" : true,
15610     
15611         /**
15612          * @event beforeselect
15613          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15614          * @param {Roo.View} this
15615          * @param {HTMLElement} node The node to be selected
15616          * @param {Array} selections Array of currently selected nodes
15617          */
15618             "beforeselect" : true,
15619         /**
15620          * @event preparedata
15621          * Fires on every row to render, to allow you to change the data.
15622          * @param {Roo.View} this
15623          * @param {Object} data to be rendered (change this)
15624          */
15625           "preparedata" : true
15626           
15627           
15628         });
15629
15630
15631
15632     this.el.on({
15633         "click": this.onClick,
15634         "dblclick": this.onDblClick,
15635         "contextmenu": this.onContextMenu,
15636         scope:this
15637     });
15638
15639     this.selections = [];
15640     this.nodes = [];
15641     this.cmp = new Roo.CompositeElementLite([]);
15642     if(this.store){
15643         this.store = Roo.factory(this.store, Roo.data);
15644         this.setStore(this.store, true);
15645     }
15646     
15647     if ( this.footer && this.footer.xtype) {
15648            
15649          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15650         
15651         this.footer.dataSource = this.store;
15652         this.footer.container = fctr;
15653         this.footer = Roo.factory(this.footer, Roo);
15654         fctr.insertFirst(this.el);
15655         
15656         // this is a bit insane - as the paging toolbar seems to detach the el..
15657 //        dom.parentNode.parentNode.parentNode
15658          // they get detached?
15659     }
15660     
15661     
15662     Roo.View.superclass.constructor.call(this);
15663     
15664     
15665 };
15666
15667 Roo.extend(Roo.View, Roo.util.Observable, {
15668     
15669      /**
15670      * @cfg {Roo.data.Store} store Data store to load data from.
15671      */
15672     store : false,
15673     
15674     /**
15675      * @cfg {String|Roo.Element} el The container element.
15676      */
15677     el : '',
15678     
15679     /**
15680      * @cfg {String|Roo.Template} tpl The template used by this View 
15681      */
15682     tpl : false,
15683     /**
15684      * @cfg {String} dataName the named area of the template to use as the data area
15685      *                          Works with domtemplates roo-name="name"
15686      */
15687     dataName: false,
15688     /**
15689      * @cfg {String} selectedClass The css class to add to selected nodes
15690      */
15691     selectedClass : "x-view-selected",
15692      /**
15693      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15694      */
15695     emptyText : "",
15696     
15697     /**
15698      * @cfg {String} text to display on mask (default Loading)
15699      */
15700     mask : false,
15701     /**
15702      * @cfg {Boolean} multiSelect Allow multiple selection
15703      */
15704     multiSelect : false,
15705     /**
15706      * @cfg {Boolean} singleSelect Allow single selection
15707      */
15708     singleSelect:  false,
15709     
15710     /**
15711      * @cfg {Boolean} toggleSelect - selecting 
15712      */
15713     toggleSelect : false,
15714     
15715     /**
15716      * @cfg {Boolean} tickable - selecting 
15717      */
15718     tickable : false,
15719     
15720     /**
15721      * Returns the element this view is bound to.
15722      * @return {Roo.Element}
15723      */
15724     getEl : function(){
15725         return this.wrapEl;
15726     },
15727     
15728     
15729
15730     /**
15731      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15732      */
15733     refresh : function(){
15734         //Roo.log('refresh');
15735         var t = this.tpl;
15736         
15737         // if we are using something like 'domtemplate', then
15738         // the what gets used is:
15739         // t.applySubtemplate(NAME, data, wrapping data..)
15740         // the outer template then get' applied with
15741         //     the store 'extra data'
15742         // and the body get's added to the
15743         //      roo-name="data" node?
15744         //      <span class='roo-tpl-{name}'></span> ?????
15745         
15746         
15747         
15748         this.clearSelections();
15749         this.el.update("");
15750         var html = [];
15751         var records = this.store.getRange();
15752         if(records.length < 1) {
15753             
15754             // is this valid??  = should it render a template??
15755             
15756             this.el.update(this.emptyText);
15757             return;
15758         }
15759         var el = this.el;
15760         if (this.dataName) {
15761             this.el.update(t.apply(this.store.meta)); //????
15762             el = this.el.child('.roo-tpl-' + this.dataName);
15763         }
15764         
15765         for(var i = 0, len = records.length; i < len; i++){
15766             var data = this.prepareData(records[i].data, i, records[i]);
15767             this.fireEvent("preparedata", this, data, i, records[i]);
15768             
15769             var d = Roo.apply({}, data);
15770             
15771             if(this.tickable){
15772                 Roo.apply(d, {'roo-id' : Roo.id()});
15773                 
15774                 var _this = this;
15775             
15776                 Roo.each(this.parent.item, function(item){
15777                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15778                         return;
15779                     }
15780                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15781                 });
15782             }
15783             
15784             html[html.length] = Roo.util.Format.trim(
15785                 this.dataName ?
15786                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15787                     t.apply(d)
15788             );
15789         }
15790         
15791         
15792         
15793         el.update(html.join(""));
15794         this.nodes = el.dom.childNodes;
15795         this.updateIndexes(0);
15796     },
15797     
15798
15799     /**
15800      * Function to override to reformat the data that is sent to
15801      * the template for each node.
15802      * DEPRICATED - use the preparedata event handler.
15803      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15804      * a JSON object for an UpdateManager bound view).
15805      */
15806     prepareData : function(data, index, record)
15807     {
15808         this.fireEvent("preparedata", this, data, index, record);
15809         return data;
15810     },
15811
15812     onUpdate : function(ds, record){
15813         // Roo.log('on update');   
15814         this.clearSelections();
15815         var index = this.store.indexOf(record);
15816         var n = this.nodes[index];
15817         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15818         n.parentNode.removeChild(n);
15819         this.updateIndexes(index, index);
15820     },
15821
15822     
15823     
15824 // --------- FIXME     
15825     onAdd : function(ds, records, index)
15826     {
15827         //Roo.log(['on Add', ds, records, index] );        
15828         this.clearSelections();
15829         if(this.nodes.length == 0){
15830             this.refresh();
15831             return;
15832         }
15833         var n = this.nodes[index];
15834         for(var i = 0, len = records.length; i < len; i++){
15835             var d = this.prepareData(records[i].data, i, records[i]);
15836             if(n){
15837                 this.tpl.insertBefore(n, d);
15838             }else{
15839                 
15840                 this.tpl.append(this.el, d);
15841             }
15842         }
15843         this.updateIndexes(index);
15844     },
15845
15846     onRemove : function(ds, record, index){
15847        // Roo.log('onRemove');
15848         this.clearSelections();
15849         var el = this.dataName  ?
15850             this.el.child('.roo-tpl-' + this.dataName) :
15851             this.el; 
15852         
15853         el.dom.removeChild(this.nodes[index]);
15854         this.updateIndexes(index);
15855     },
15856
15857     /**
15858      * Refresh an individual node.
15859      * @param {Number} index
15860      */
15861     refreshNode : function(index){
15862         this.onUpdate(this.store, this.store.getAt(index));
15863     },
15864
15865     updateIndexes : function(startIndex, endIndex){
15866         var ns = this.nodes;
15867         startIndex = startIndex || 0;
15868         endIndex = endIndex || ns.length - 1;
15869         for(var i = startIndex; i <= endIndex; i++){
15870             ns[i].nodeIndex = i;
15871         }
15872     },
15873
15874     /**
15875      * Changes the data store this view uses and refresh the view.
15876      * @param {Store} store
15877      */
15878     setStore : function(store, initial){
15879         if(!initial && this.store){
15880             this.store.un("datachanged", this.refresh);
15881             this.store.un("add", this.onAdd);
15882             this.store.un("remove", this.onRemove);
15883             this.store.un("update", this.onUpdate);
15884             this.store.un("clear", this.refresh);
15885             this.store.un("beforeload", this.onBeforeLoad);
15886             this.store.un("load", this.onLoad);
15887             this.store.un("loadexception", this.onLoad);
15888         }
15889         if(store){
15890           
15891             store.on("datachanged", this.refresh, this);
15892             store.on("add", this.onAdd, this);
15893             store.on("remove", this.onRemove, this);
15894             store.on("update", this.onUpdate, this);
15895             store.on("clear", this.refresh, this);
15896             store.on("beforeload", this.onBeforeLoad, this);
15897             store.on("load", this.onLoad, this);
15898             store.on("loadexception", this.onLoad, this);
15899         }
15900         
15901         if(store){
15902             this.refresh();
15903         }
15904     },
15905     /**
15906      * onbeforeLoad - masks the loading area.
15907      *
15908      */
15909     onBeforeLoad : function(store,opts)
15910     {
15911          //Roo.log('onBeforeLoad');   
15912         if (!opts.add) {
15913             this.el.update("");
15914         }
15915         this.el.mask(this.mask ? this.mask : "Loading" ); 
15916     },
15917     onLoad : function ()
15918     {
15919         this.el.unmask();
15920     },
15921     
15922
15923     /**
15924      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15925      * @param {HTMLElement} node
15926      * @return {HTMLElement} The template node
15927      */
15928     findItemFromChild : function(node){
15929         var el = this.dataName  ?
15930             this.el.child('.roo-tpl-' + this.dataName,true) :
15931             this.el.dom; 
15932         
15933         if(!node || node.parentNode == el){
15934                     return node;
15935             }
15936             var p = node.parentNode;
15937             while(p && p != el){
15938             if(p.parentNode == el){
15939                 return p;
15940             }
15941             p = p.parentNode;
15942         }
15943             return null;
15944     },
15945
15946     /** @ignore */
15947     onClick : function(e){
15948         var item = this.findItemFromChild(e.getTarget());
15949         if(item){
15950             var index = this.indexOf(item);
15951             if(this.onItemClick(item, index, e) !== false){
15952                 this.fireEvent("click", this, index, item, e);
15953             }
15954         }else{
15955             this.clearSelections();
15956         }
15957     },
15958
15959     /** @ignore */
15960     onContextMenu : function(e){
15961         var item = this.findItemFromChild(e.getTarget());
15962         if(item){
15963             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15964         }
15965     },
15966
15967     /** @ignore */
15968     onDblClick : function(e){
15969         var item = this.findItemFromChild(e.getTarget());
15970         if(item){
15971             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15972         }
15973     },
15974
15975     onItemClick : function(item, index, e)
15976     {
15977         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15978             return false;
15979         }
15980         if (this.toggleSelect) {
15981             var m = this.isSelected(item) ? 'unselect' : 'select';
15982             //Roo.log(m);
15983             var _t = this;
15984             _t[m](item, true, false);
15985             return true;
15986         }
15987         if(this.multiSelect || this.singleSelect){
15988             if(this.multiSelect && e.shiftKey && this.lastSelection){
15989                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15990             }else{
15991                 this.select(item, this.multiSelect && e.ctrlKey);
15992                 this.lastSelection = item;
15993             }
15994             
15995             if(!this.tickable){
15996                 e.preventDefault();
15997             }
15998             
15999         }
16000         return true;
16001     },
16002
16003     /**
16004      * Get the number of selected nodes.
16005      * @return {Number}
16006      */
16007     getSelectionCount : function(){
16008         return this.selections.length;
16009     },
16010
16011     /**
16012      * Get the currently selected nodes.
16013      * @return {Array} An array of HTMLElements
16014      */
16015     getSelectedNodes : function(){
16016         return this.selections;
16017     },
16018
16019     /**
16020      * Get the indexes of the selected nodes.
16021      * @return {Array}
16022      */
16023     getSelectedIndexes : function(){
16024         var indexes = [], s = this.selections;
16025         for(var i = 0, len = s.length; i < len; i++){
16026             indexes.push(s[i].nodeIndex);
16027         }
16028         return indexes;
16029     },
16030
16031     /**
16032      * Clear all selections
16033      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16034      */
16035     clearSelections : function(suppressEvent){
16036         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16037             this.cmp.elements = this.selections;
16038             this.cmp.removeClass(this.selectedClass);
16039             this.selections = [];
16040             if(!suppressEvent){
16041                 this.fireEvent("selectionchange", this, this.selections);
16042             }
16043         }
16044     },
16045
16046     /**
16047      * Returns true if the passed node is selected
16048      * @param {HTMLElement/Number} node The node or node index
16049      * @return {Boolean}
16050      */
16051     isSelected : function(node){
16052         var s = this.selections;
16053         if(s.length < 1){
16054             return false;
16055         }
16056         node = this.getNode(node);
16057         return s.indexOf(node) !== -1;
16058     },
16059
16060     /**
16061      * Selects nodes.
16062      * @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
16063      * @param {Boolean} keepExisting (optional) true to keep existing selections
16064      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16065      */
16066     select : function(nodeInfo, keepExisting, suppressEvent){
16067         if(nodeInfo instanceof Array){
16068             if(!keepExisting){
16069                 this.clearSelections(true);
16070             }
16071             for(var i = 0, len = nodeInfo.length; i < len; i++){
16072                 this.select(nodeInfo[i], true, true);
16073             }
16074             return;
16075         } 
16076         var node = this.getNode(nodeInfo);
16077         if(!node || this.isSelected(node)){
16078             return; // already selected.
16079         }
16080         if(!keepExisting){
16081             this.clearSelections(true);
16082         }
16083         
16084         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16085             Roo.fly(node).addClass(this.selectedClass);
16086             this.selections.push(node);
16087             if(!suppressEvent){
16088                 this.fireEvent("selectionchange", this, this.selections);
16089             }
16090         }
16091         
16092         
16093     },
16094       /**
16095      * Unselects nodes.
16096      * @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
16097      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16099      */
16100     unselect : function(nodeInfo, keepExisting, suppressEvent)
16101     {
16102         if(nodeInfo instanceof Array){
16103             Roo.each(this.selections, function(s) {
16104                 this.unselect(s, nodeInfo);
16105             }, this);
16106             return;
16107         }
16108         var node = this.getNode(nodeInfo);
16109         if(!node || !this.isSelected(node)){
16110             //Roo.log("not selected");
16111             return; // not selected.
16112         }
16113         // fireevent???
16114         var ns = [];
16115         Roo.each(this.selections, function(s) {
16116             if (s == node ) {
16117                 Roo.fly(node).removeClass(this.selectedClass);
16118
16119                 return;
16120             }
16121             ns.push(s);
16122         },this);
16123         
16124         this.selections= ns;
16125         this.fireEvent("selectionchange", this, this.selections);
16126     },
16127
16128     /**
16129      * Gets a template node.
16130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16131      * @return {HTMLElement} The node or null if it wasn't found
16132      */
16133     getNode : function(nodeInfo){
16134         if(typeof nodeInfo == "string"){
16135             return document.getElementById(nodeInfo);
16136         }else if(typeof nodeInfo == "number"){
16137             return this.nodes[nodeInfo];
16138         }
16139         return nodeInfo;
16140     },
16141
16142     /**
16143      * Gets a range template nodes.
16144      * @param {Number} startIndex
16145      * @param {Number} endIndex
16146      * @return {Array} An array of nodes
16147      */
16148     getNodes : function(start, end){
16149         var ns = this.nodes;
16150         start = start || 0;
16151         end = typeof end == "undefined" ? ns.length - 1 : end;
16152         var nodes = [];
16153         if(start <= end){
16154             for(var i = start; i <= end; i++){
16155                 nodes.push(ns[i]);
16156             }
16157         } else{
16158             for(var i = start; i >= end; i--){
16159                 nodes.push(ns[i]);
16160             }
16161         }
16162         return nodes;
16163     },
16164
16165     /**
16166      * Finds the index of the passed node
16167      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16168      * @return {Number} The index of the node or -1
16169      */
16170     indexOf : function(node){
16171         node = this.getNode(node);
16172         if(typeof node.nodeIndex == "number"){
16173             return node.nodeIndex;
16174         }
16175         var ns = this.nodes;
16176         for(var i = 0, len = ns.length; i < len; i++){
16177             if(ns[i] == node){
16178                 return i;
16179             }
16180         }
16181         return -1;
16182     }
16183 });
16184 /*
16185  * - LGPL
16186  *
16187  * based on jquery fullcalendar
16188  * 
16189  */
16190
16191 Roo.bootstrap = Roo.bootstrap || {};
16192 /**
16193  * @class Roo.bootstrap.Calendar
16194  * @extends Roo.bootstrap.Component
16195  * Bootstrap Calendar class
16196  * @cfg {Boolean} loadMask (true|false) default false
16197  * @cfg {Object} header generate the user specific header of the calendar, default false
16198
16199  * @constructor
16200  * Create a new Container
16201  * @param {Object} config The config object
16202  */
16203
16204
16205
16206 Roo.bootstrap.Calendar = function(config){
16207     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16208      this.addEvents({
16209         /**
16210              * @event select
16211              * Fires when a date is selected
16212              * @param {DatePicker} this
16213              * @param {Date} date The selected date
16214              */
16215         'select': true,
16216         /**
16217              * @event monthchange
16218              * Fires when the displayed month changes 
16219              * @param {DatePicker} this
16220              * @param {Date} date The selected month
16221              */
16222         'monthchange': true,
16223         /**
16224              * @event evententer
16225              * Fires when mouse over an event
16226              * @param {Calendar} this
16227              * @param {event} Event
16228              */
16229         'evententer': true,
16230         /**
16231              * @event eventleave
16232              * Fires when the mouse leaves an
16233              * @param {Calendar} this
16234              * @param {event}
16235              */
16236         'eventleave': true,
16237         /**
16238              * @event eventclick
16239              * Fires when the mouse click an
16240              * @param {Calendar} this
16241              * @param {event}
16242              */
16243         'eventclick': true
16244         
16245     });
16246
16247 };
16248
16249 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16250     
16251      /**
16252      * @cfg {Number} startDay
16253      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16254      */
16255     startDay : 0,
16256     
16257     loadMask : false,
16258     
16259     header : false,
16260       
16261     getAutoCreate : function(){
16262         
16263         
16264         var fc_button = function(name, corner, style, content ) {
16265             return Roo.apply({},{
16266                 tag : 'span',
16267                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16268                          (corner.length ?
16269                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16270                             ''
16271                         ),
16272                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16273                 unselectable: 'on'
16274             });
16275         };
16276         
16277         var header = {};
16278         
16279         if(!this.header){
16280             header = {
16281                 tag : 'table',
16282                 cls : 'fc-header',
16283                 style : 'width:100%',
16284                 cn : [
16285                     {
16286                         tag: 'tr',
16287                         cn : [
16288                             {
16289                                 tag : 'td',
16290                                 cls : 'fc-header-left',
16291                                 cn : [
16292                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16293                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16294                                     { tag: 'span', cls: 'fc-header-space' },
16295                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16296
16297
16298                                 ]
16299                             },
16300
16301                             {
16302                                 tag : 'td',
16303                                 cls : 'fc-header-center',
16304                                 cn : [
16305                                     {
16306                                         tag: 'span',
16307                                         cls: 'fc-header-title',
16308                                         cn : {
16309                                             tag: 'H2',
16310                                             html : 'month / year'
16311                                         }
16312                                     }
16313
16314                                 ]
16315                             },
16316                             {
16317                                 tag : 'td',
16318                                 cls : 'fc-header-right',
16319                                 cn : [
16320                               /*      fc_button('month', 'left', '', 'month' ),
16321                                     fc_button('week', '', '', 'week' ),
16322                                     fc_button('day', 'right', '', 'day' )
16323                                 */    
16324
16325                                 ]
16326                             }
16327
16328                         ]
16329                     }
16330                 ]
16331             };
16332         }
16333         
16334         header = this.header;
16335         
16336        
16337         var cal_heads = function() {
16338             var ret = [];
16339             // fixme - handle this.
16340             
16341             for (var i =0; i < Date.dayNames.length; i++) {
16342                 var d = Date.dayNames[i];
16343                 ret.push({
16344                     tag: 'th',
16345                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16346                     html : d.substring(0,3)
16347                 });
16348                 
16349             }
16350             ret[0].cls += ' fc-first';
16351             ret[6].cls += ' fc-last';
16352             return ret;
16353         };
16354         var cal_cell = function(n) {
16355             return  {
16356                 tag: 'td',
16357                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16358                 cn : [
16359                     {
16360                         cn : [
16361                             {
16362                                 cls: 'fc-day-number',
16363                                 html: 'D'
16364                             },
16365                             {
16366                                 cls: 'fc-day-content',
16367                              
16368                                 cn : [
16369                                      {
16370                                         style: 'position: relative;' // height: 17px;
16371                                     }
16372                                 ]
16373                             }
16374                             
16375                             
16376                         ]
16377                     }
16378                 ]
16379                 
16380             }
16381         };
16382         var cal_rows = function() {
16383             
16384             var ret = [];
16385             for (var r = 0; r < 6; r++) {
16386                 var row= {
16387                     tag : 'tr',
16388                     cls : 'fc-week',
16389                     cn : []
16390                 };
16391                 
16392                 for (var i =0; i < Date.dayNames.length; i++) {
16393                     var d = Date.dayNames[i];
16394                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16395
16396                 }
16397                 row.cn[0].cls+=' fc-first';
16398                 row.cn[0].cn[0].style = 'min-height:90px';
16399                 row.cn[6].cls+=' fc-last';
16400                 ret.push(row);
16401                 
16402             }
16403             ret[0].cls += ' fc-first';
16404             ret[4].cls += ' fc-prev-last';
16405             ret[5].cls += ' fc-last';
16406             return ret;
16407             
16408         };
16409         
16410         var cal_table = {
16411             tag: 'table',
16412             cls: 'fc-border-separate',
16413             style : 'width:100%',
16414             cellspacing  : 0,
16415             cn : [
16416                 { 
16417                     tag: 'thead',
16418                     cn : [
16419                         { 
16420                             tag: 'tr',
16421                             cls : 'fc-first fc-last',
16422                             cn : cal_heads()
16423                         }
16424                     ]
16425                 },
16426                 { 
16427                     tag: 'tbody',
16428                     cn : cal_rows()
16429                 }
16430                   
16431             ]
16432         };
16433          
16434          var cfg = {
16435             cls : 'fc fc-ltr',
16436             cn : [
16437                 header,
16438                 {
16439                     cls : 'fc-content',
16440                     style : "position: relative;",
16441                     cn : [
16442                         {
16443                             cls : 'fc-view fc-view-month fc-grid',
16444                             style : 'position: relative',
16445                             unselectable : 'on',
16446                             cn : [
16447                                 {
16448                                     cls : 'fc-event-container',
16449                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16450                                 },
16451                                 cal_table
16452                             ]
16453                         }
16454                     ]
16455     
16456                 }
16457            ] 
16458             
16459         };
16460         
16461          
16462         
16463         return cfg;
16464     },
16465     
16466     
16467     initEvents : function()
16468     {
16469         if(!this.store){
16470             throw "can not find store for calendar";
16471         }
16472         
16473         var mark = {
16474             tag: "div",
16475             cls:"x-dlg-mask",
16476             style: "text-align:center",
16477             cn: [
16478                 {
16479                     tag: "div",
16480                     style: "background-color:white;width:50%;margin:250 auto",
16481                     cn: [
16482                         {
16483                             tag: "img",
16484                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16485                         },
16486                         {
16487                             tag: "span",
16488                             html: "Loading"
16489                         }
16490                         
16491                     ]
16492                 }
16493             ]
16494         };
16495         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16496         
16497         var size = this.el.select('.fc-content', true).first().getSize();
16498         this.maskEl.setSize(size.width, size.height);
16499         this.maskEl.enableDisplayMode("block");
16500         if(!this.loadMask){
16501             this.maskEl.hide();
16502         }
16503         
16504         this.store = Roo.factory(this.store, Roo.data);
16505         this.store.on('load', this.onLoad, this);
16506         this.store.on('beforeload', this.onBeforeLoad, this);
16507         
16508         this.resize();
16509         
16510         this.cells = this.el.select('.fc-day',true);
16511         //Roo.log(this.cells);
16512         this.textNodes = this.el.query('.fc-day-number');
16513         this.cells.addClassOnOver('fc-state-hover');
16514         
16515         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16516         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16517         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16518         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16519         
16520         this.on('monthchange', this.onMonthChange, this);
16521         
16522         this.update(new Date().clearTime());
16523     },
16524     
16525     resize : function() {
16526         var sz  = this.el.getSize();
16527         
16528         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16529         this.el.select('.fc-day-content div',true).setHeight(34);
16530     },
16531     
16532     
16533     // private
16534     showPrevMonth : function(e){
16535         this.update(this.activeDate.add("mo", -1));
16536     },
16537     showToday : function(e){
16538         this.update(new Date().clearTime());
16539     },
16540     // private
16541     showNextMonth : function(e){
16542         this.update(this.activeDate.add("mo", 1));
16543     },
16544
16545     // private
16546     showPrevYear : function(){
16547         this.update(this.activeDate.add("y", -1));
16548     },
16549
16550     // private
16551     showNextYear : function(){
16552         this.update(this.activeDate.add("y", 1));
16553     },
16554
16555     
16556    // private
16557     update : function(date)
16558     {
16559         var vd = this.activeDate;
16560         this.activeDate = date;
16561 //        if(vd && this.el){
16562 //            var t = date.getTime();
16563 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16564 //                Roo.log('using add remove');
16565 //                
16566 //                this.fireEvent('monthchange', this, date);
16567 //                
16568 //                this.cells.removeClass("fc-state-highlight");
16569 //                this.cells.each(function(c){
16570 //                   if(c.dateValue == t){
16571 //                       c.addClass("fc-state-highlight");
16572 //                       setTimeout(function(){
16573 //                            try{c.dom.firstChild.focus();}catch(e){}
16574 //                       }, 50);
16575 //                       return false;
16576 //                   }
16577 //                   return true;
16578 //                });
16579 //                return;
16580 //            }
16581 //        }
16582         
16583         var days = date.getDaysInMonth();
16584         
16585         var firstOfMonth = date.getFirstDateOfMonth();
16586         var startingPos = firstOfMonth.getDay()-this.startDay;
16587         
16588         if(startingPos < this.startDay){
16589             startingPos += 7;
16590         }
16591         
16592         var pm = date.add(Date.MONTH, -1);
16593         var prevStart = pm.getDaysInMonth()-startingPos;
16594 //        
16595         this.cells = this.el.select('.fc-day',true);
16596         this.textNodes = this.el.query('.fc-day-number');
16597         this.cells.addClassOnOver('fc-state-hover');
16598         
16599         var cells = this.cells.elements;
16600         var textEls = this.textNodes;
16601         
16602         Roo.each(cells, function(cell){
16603             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16604         });
16605         
16606         days += startingPos;
16607
16608         // convert everything to numbers so it's fast
16609         var day = 86400000;
16610         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16611         //Roo.log(d);
16612         //Roo.log(pm);
16613         //Roo.log(prevStart);
16614         
16615         var today = new Date().clearTime().getTime();
16616         var sel = date.clearTime().getTime();
16617         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16618         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16619         var ddMatch = this.disabledDatesRE;
16620         var ddText = this.disabledDatesText;
16621         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16622         var ddaysText = this.disabledDaysText;
16623         var format = this.format;
16624         
16625         var setCellClass = function(cal, cell){
16626             cell.row = 0;
16627             cell.events = [];
16628             cell.more = [];
16629             //Roo.log('set Cell Class');
16630             cell.title = "";
16631             var t = d.getTime();
16632             
16633             //Roo.log(d);
16634             
16635             cell.dateValue = t;
16636             if(t == today){
16637                 cell.className += " fc-today";
16638                 cell.className += " fc-state-highlight";
16639                 cell.title = cal.todayText;
16640             }
16641             if(t == sel){
16642                 // disable highlight in other month..
16643                 //cell.className += " fc-state-highlight";
16644                 
16645             }
16646             // disabling
16647             if(t < min) {
16648                 cell.className = " fc-state-disabled";
16649                 cell.title = cal.minText;
16650                 return;
16651             }
16652             if(t > max) {
16653                 cell.className = " fc-state-disabled";
16654                 cell.title = cal.maxText;
16655                 return;
16656             }
16657             if(ddays){
16658                 if(ddays.indexOf(d.getDay()) != -1){
16659                     cell.title = ddaysText;
16660                     cell.className = " fc-state-disabled";
16661                 }
16662             }
16663             if(ddMatch && format){
16664                 var fvalue = d.dateFormat(format);
16665                 if(ddMatch.test(fvalue)){
16666                     cell.title = ddText.replace("%0", fvalue);
16667                     cell.className = " fc-state-disabled";
16668                 }
16669             }
16670             
16671             if (!cell.initialClassName) {
16672                 cell.initialClassName = cell.dom.className;
16673             }
16674             
16675             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16676         };
16677
16678         var i = 0;
16679         
16680         for(; i < startingPos; i++) {
16681             textEls[i].innerHTML = (++prevStart);
16682             d.setDate(d.getDate()+1);
16683             
16684             cells[i].className = "fc-past fc-other-month";
16685             setCellClass(this, cells[i]);
16686         }
16687         
16688         var intDay = 0;
16689         
16690         for(; i < days; i++){
16691             intDay = i - startingPos + 1;
16692             textEls[i].innerHTML = (intDay);
16693             d.setDate(d.getDate()+1);
16694             
16695             cells[i].className = ''; // "x-date-active";
16696             setCellClass(this, cells[i]);
16697         }
16698         var extraDays = 0;
16699         
16700         for(; i < 42; i++) {
16701             textEls[i].innerHTML = (++extraDays);
16702             d.setDate(d.getDate()+1);
16703             
16704             cells[i].className = "fc-future fc-other-month";
16705             setCellClass(this, cells[i]);
16706         }
16707         
16708         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16709         
16710         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16711         
16712         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16713         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16714         
16715         if(totalRows != 6){
16716             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16717             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16718         }
16719         
16720         this.fireEvent('monthchange', this, date);
16721         
16722         
16723         /*
16724         if(!this.internalRender){
16725             var main = this.el.dom.firstChild;
16726             var w = main.offsetWidth;
16727             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16728             Roo.fly(main).setWidth(w);
16729             this.internalRender = true;
16730             // opera does not respect the auto grow header center column
16731             // then, after it gets a width opera refuses to recalculate
16732             // without a second pass
16733             if(Roo.isOpera && !this.secondPass){
16734                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16735                 this.secondPass = true;
16736                 this.update.defer(10, this, [date]);
16737             }
16738         }
16739         */
16740         
16741     },
16742     
16743     findCell : function(dt) {
16744         dt = dt.clearTime().getTime();
16745         var ret = false;
16746         this.cells.each(function(c){
16747             //Roo.log("check " +c.dateValue + '?=' + dt);
16748             if(c.dateValue == dt){
16749                 ret = c;
16750                 return false;
16751             }
16752             return true;
16753         });
16754         
16755         return ret;
16756     },
16757     
16758     findCells : function(ev) {
16759         var s = ev.start.clone().clearTime().getTime();
16760        // Roo.log(s);
16761         var e= ev.end.clone().clearTime().getTime();
16762        // Roo.log(e);
16763         var ret = [];
16764         this.cells.each(function(c){
16765              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16766             
16767             if(c.dateValue > e){
16768                 return ;
16769             }
16770             if(c.dateValue < s){
16771                 return ;
16772             }
16773             ret.push(c);
16774         });
16775         
16776         return ret;    
16777     },
16778     
16779 //    findBestRow: function(cells)
16780 //    {
16781 //        var ret = 0;
16782 //        
16783 //        for (var i =0 ; i < cells.length;i++) {
16784 //            ret  = Math.max(cells[i].rows || 0,ret);
16785 //        }
16786 //        return ret;
16787 //        
16788 //    },
16789     
16790     
16791     addItem : function(ev)
16792     {
16793         // look for vertical location slot in
16794         var cells = this.findCells(ev);
16795         
16796 //        ev.row = this.findBestRow(cells);
16797         
16798         // work out the location.
16799         
16800         var crow = false;
16801         var rows = [];
16802         for(var i =0; i < cells.length; i++) {
16803             
16804             cells[i].row = cells[0].row;
16805             
16806             if(i == 0){
16807                 cells[i].row = cells[i].row + 1;
16808             }
16809             
16810             if (!crow) {
16811                 crow = {
16812                     start : cells[i],
16813                     end :  cells[i]
16814                 };
16815                 continue;
16816             }
16817             if (crow.start.getY() == cells[i].getY()) {
16818                 // on same row.
16819                 crow.end = cells[i];
16820                 continue;
16821             }
16822             // different row.
16823             rows.push(crow);
16824             crow = {
16825                 start: cells[i],
16826                 end : cells[i]
16827             };
16828             
16829         }
16830         
16831         rows.push(crow);
16832         ev.els = [];
16833         ev.rows = rows;
16834         ev.cells = cells;
16835         
16836         cells[0].events.push(ev);
16837         
16838         this.calevents.push(ev);
16839     },
16840     
16841     clearEvents: function() {
16842         
16843         if(!this.calevents){
16844             return;
16845         }
16846         
16847         Roo.each(this.cells.elements, function(c){
16848             c.row = 0;
16849             c.events = [];
16850             c.more = [];
16851         });
16852         
16853         Roo.each(this.calevents, function(e) {
16854             Roo.each(e.els, function(el) {
16855                 el.un('mouseenter' ,this.onEventEnter, this);
16856                 el.un('mouseleave' ,this.onEventLeave, this);
16857                 el.remove();
16858             },this);
16859         },this);
16860         
16861         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16862             e.remove();
16863         });
16864         
16865     },
16866     
16867     renderEvents: function()
16868     {   
16869         var _this = this;
16870         
16871         this.cells.each(function(c) {
16872             
16873             if(c.row < 5){
16874                 return;
16875             }
16876             
16877             var ev = c.events;
16878             
16879             var r = 4;
16880             if(c.row != c.events.length){
16881                 r = 4 - (4 - (c.row - c.events.length));
16882             }
16883             
16884             c.events = ev.slice(0, r);
16885             c.more = ev.slice(r);
16886             
16887             if(c.more.length && c.more.length == 1){
16888                 c.events.push(c.more.pop());
16889             }
16890             
16891             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16892             
16893         });
16894             
16895         this.cells.each(function(c) {
16896             
16897             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16898             
16899             
16900             for (var e = 0; e < c.events.length; e++){
16901                 var ev = c.events[e];
16902                 var rows = ev.rows;
16903                 
16904                 for(var i = 0; i < rows.length; i++) {
16905                 
16906                     // how many rows should it span..
16907
16908                     var  cfg = {
16909                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16910                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16911
16912                         unselectable : "on",
16913                         cn : [
16914                             {
16915                                 cls: 'fc-event-inner',
16916                                 cn : [
16917     //                                {
16918     //                                  tag:'span',
16919     //                                  cls: 'fc-event-time',
16920     //                                  html : cells.length > 1 ? '' : ev.time
16921     //                                },
16922                                     {
16923                                       tag:'span',
16924                                       cls: 'fc-event-title',
16925                                       html : String.format('{0}', ev.title)
16926                                     }
16927
16928
16929                                 ]
16930                             },
16931                             {
16932                                 cls: 'ui-resizable-handle ui-resizable-e',
16933                                 html : '&nbsp;&nbsp;&nbsp'
16934                             }
16935
16936                         ]
16937                     };
16938
16939                     if (i == 0) {
16940                         cfg.cls += ' fc-event-start';
16941                     }
16942                     if ((i+1) == rows.length) {
16943                         cfg.cls += ' fc-event-end';
16944                     }
16945
16946                     var ctr = _this.el.select('.fc-event-container',true).first();
16947                     var cg = ctr.createChild(cfg);
16948
16949                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16950                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16951
16952                     var r = (c.more.length) ? 1 : 0;
16953                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16954                     cg.setWidth(ebox.right - sbox.x -2);
16955
16956                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16957                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16958                     cg.on('click', _this.onEventClick, _this, ev);
16959
16960                     ev.els.push(cg);
16961                     
16962                 }
16963                 
16964             }
16965             
16966             
16967             if(c.more.length){
16968                 var  cfg = {
16969                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16970                     style : 'position: absolute',
16971                     unselectable : "on",
16972                     cn : [
16973                         {
16974                             cls: 'fc-event-inner',
16975                             cn : [
16976                                 {
16977                                   tag:'span',
16978                                   cls: 'fc-event-title',
16979                                   html : 'More'
16980                                 }
16981
16982
16983                             ]
16984                         },
16985                         {
16986                             cls: 'ui-resizable-handle ui-resizable-e',
16987                             html : '&nbsp;&nbsp;&nbsp'
16988                         }
16989
16990                     ]
16991                 };
16992
16993                 var ctr = _this.el.select('.fc-event-container',true).first();
16994                 var cg = ctr.createChild(cfg);
16995
16996                 var sbox = c.select('.fc-day-content',true).first().getBox();
16997                 var ebox = c.select('.fc-day-content',true).first().getBox();
16998                 //Roo.log(cg);
16999                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17000                 cg.setWidth(ebox.right - sbox.x -2);
17001
17002                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17003                 
17004             }
17005             
17006         });
17007         
17008         
17009         
17010     },
17011     
17012     onEventEnter: function (e, el,event,d) {
17013         this.fireEvent('evententer', this, el, event);
17014     },
17015     
17016     onEventLeave: function (e, el,event,d) {
17017         this.fireEvent('eventleave', this, el, event);
17018     },
17019     
17020     onEventClick: function (e, el,event,d) {
17021         this.fireEvent('eventclick', this, el, event);
17022     },
17023     
17024     onMonthChange: function () {
17025         this.store.load();
17026     },
17027     
17028     onMoreEventClick: function(e, el, more)
17029     {
17030         var _this = this;
17031         
17032         this.calpopover.placement = 'right';
17033         this.calpopover.setTitle('More');
17034         
17035         this.calpopover.setContent('');
17036         
17037         var ctr = this.calpopover.el.select('.popover-content', true).first();
17038         
17039         Roo.each(more, function(m){
17040             var cfg = {
17041                 cls : 'fc-event-hori fc-event-draggable',
17042                 html : m.title
17043             };
17044             var cg = ctr.createChild(cfg);
17045             
17046             cg.on('click', _this.onEventClick, _this, m);
17047         });
17048         
17049         this.calpopover.show(el);
17050         
17051         
17052     },
17053     
17054     onLoad: function () 
17055     {   
17056         this.calevents = [];
17057         var cal = this;
17058         
17059         if(this.store.getCount() > 0){
17060             this.store.data.each(function(d){
17061                cal.addItem({
17062                     id : d.data.id,
17063                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17064                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17065                     time : d.data.start_time,
17066                     title : d.data.title,
17067                     description : d.data.description,
17068                     venue : d.data.venue
17069                 });
17070             });
17071         }
17072         
17073         this.renderEvents();
17074         
17075         if(this.calevents.length && this.loadMask){
17076             this.maskEl.hide();
17077         }
17078     },
17079     
17080     onBeforeLoad: function()
17081     {
17082         this.clearEvents();
17083         if(this.loadMask){
17084             this.maskEl.show();
17085         }
17086     }
17087 });
17088
17089  
17090  /*
17091  * - LGPL
17092  *
17093  * element
17094  * 
17095  */
17096
17097 /**
17098  * @class Roo.bootstrap.Popover
17099  * @extends Roo.bootstrap.Component
17100  * Bootstrap Popover class
17101  * @cfg {String} html contents of the popover   (or false to use children..)
17102  * @cfg {String} title of popover (or false to hide)
17103  * @cfg {String} placement how it is placed
17104  * @cfg {String} trigger click || hover (or false to trigger manually)
17105  * @cfg {String} over what (parent or false to trigger manually.)
17106  * @cfg {Number} delay - delay before showing
17107  
17108  * @constructor
17109  * Create a new Popover
17110  * @param {Object} config The config object
17111  */
17112
17113 Roo.bootstrap.Popover = function(config){
17114     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17115     
17116     this.addEvents({
17117         // raw events
17118          /**
17119          * @event show
17120          * After the popover show
17121          * 
17122          * @param {Roo.bootstrap.Popover} this
17123          */
17124         "show" : true,
17125         /**
17126          * @event hide
17127          * After the popover hide
17128          * 
17129          * @param {Roo.bootstrap.Popover} this
17130          */
17131         "hide" : true
17132     });
17133 };
17134
17135 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17136     
17137     title: 'Fill in a title',
17138     html: false,
17139     
17140     placement : 'right',
17141     trigger : 'hover', // hover
17142     
17143     delay : 0,
17144     
17145     over: 'parent',
17146     
17147     can_build_overlaid : false,
17148     
17149     getChildContainer : function()
17150     {
17151         return this.el.select('.popover-content',true).first();
17152     },
17153     
17154     getAutoCreate : function(){
17155          
17156         var cfg = {
17157            cls : 'popover roo-dynamic',
17158            style: 'display:block',
17159            cn : [
17160                 {
17161                     cls : 'arrow'
17162                 },
17163                 {
17164                     cls : 'popover-inner',
17165                     cn : [
17166                         {
17167                             tag: 'h3',
17168                             cls: 'popover-title',
17169                             html : this.title
17170                         },
17171                         {
17172                             cls : 'popover-content',
17173                             html : this.html
17174                         }
17175                     ]
17176                     
17177                 }
17178            ]
17179         };
17180         
17181         return cfg;
17182     },
17183     setTitle: function(str)
17184     {
17185         this.title = str;
17186         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17187     },
17188     setContent: function(str)
17189     {
17190         this.html = str;
17191         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17192     },
17193     // as it get's added to the bottom of the page.
17194     onRender : function(ct, position)
17195     {
17196         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17197         if(!this.el){
17198             var cfg = Roo.apply({},  this.getAutoCreate());
17199             cfg.id = Roo.id();
17200             
17201             if (this.cls) {
17202                 cfg.cls += ' ' + this.cls;
17203             }
17204             if (this.style) {
17205                 cfg.style = this.style;
17206             }
17207             //Roo.log("adding to ");
17208             this.el = Roo.get(document.body).createChild(cfg, position);
17209 //            Roo.log(this.el);
17210         }
17211         this.initEvents();
17212     },
17213     
17214     initEvents : function()
17215     {
17216         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17217         this.el.enableDisplayMode('block');
17218         this.el.hide();
17219         if (this.over === false) {
17220             return; 
17221         }
17222         if (this.triggers === false) {
17223             return;
17224         }
17225         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17226         var triggers = this.trigger ? this.trigger.split(' ') : [];
17227         Roo.each(triggers, function(trigger) {
17228         
17229             if (trigger == 'click') {
17230                 on_el.on('click', this.toggle, this);
17231             } else if (trigger != 'manual') {
17232                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17233                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17234       
17235                 on_el.on(eventIn  ,this.enter, this);
17236                 on_el.on(eventOut, this.leave, this);
17237             }
17238         }, this);
17239         
17240     },
17241     
17242     
17243     // private
17244     timeout : null,
17245     hoverState : null,
17246     
17247     toggle : function () {
17248         this.hoverState == 'in' ? this.leave() : this.enter();
17249     },
17250     
17251     enter : function () {
17252         
17253         clearTimeout(this.timeout);
17254     
17255         this.hoverState = 'in';
17256     
17257         if (!this.delay || !this.delay.show) {
17258             this.show();
17259             return;
17260         }
17261         var _t = this;
17262         this.timeout = setTimeout(function () {
17263             if (_t.hoverState == 'in') {
17264                 _t.show();
17265             }
17266         }, this.delay.show)
17267     },
17268     
17269     leave : function() {
17270         clearTimeout(this.timeout);
17271     
17272         this.hoverState = 'out';
17273     
17274         if (!this.delay || !this.delay.hide) {
17275             this.hide();
17276             return;
17277         }
17278         var _t = this;
17279         this.timeout = setTimeout(function () {
17280             if (_t.hoverState == 'out') {
17281                 _t.hide();
17282             }
17283         }, this.delay.hide)
17284     },
17285     
17286     show : function (on_el)
17287     {
17288         if (!on_el) {
17289             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17290         }
17291         
17292         // set content.
17293         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17294         if (this.html !== false) {
17295             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17296         }
17297         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17298         if (!this.title.length) {
17299             this.el.select('.popover-title',true).hide();
17300         }
17301         
17302         var placement = typeof this.placement == 'function' ?
17303             this.placement.call(this, this.el, on_el) :
17304             this.placement;
17305             
17306         var autoToken = /\s?auto?\s?/i;
17307         var autoPlace = autoToken.test(placement);
17308         if (autoPlace) {
17309             placement = placement.replace(autoToken, '') || 'top';
17310         }
17311         
17312         //this.el.detach()
17313         //this.el.setXY([0,0]);
17314         this.el.show();
17315         this.el.dom.style.display='block';
17316         this.el.addClass(placement);
17317         
17318         //this.el.appendTo(on_el);
17319         
17320         var p = this.getPosition();
17321         var box = this.el.getBox();
17322         
17323         if (autoPlace) {
17324             // fixme..
17325         }
17326         var align = Roo.bootstrap.Popover.alignment[placement];
17327         this.el.alignTo(on_el, align[0],align[1]);
17328         //var arrow = this.el.select('.arrow',true).first();
17329         //arrow.set(align[2], 
17330         
17331         this.el.addClass('in');
17332         
17333         
17334         if (this.el.hasClass('fade')) {
17335             // fade it?
17336         }
17337         
17338         this.hoverState = 'in';
17339         
17340         this.fireEvent('show', this);
17341         
17342     },
17343     hide : function()
17344     {
17345         this.el.setXY([0,0]);
17346         this.el.removeClass('in');
17347         this.el.hide();
17348         this.hoverState = null;
17349         
17350         this.fireEvent('hide', this);
17351     }
17352     
17353 });
17354
17355 Roo.bootstrap.Popover.alignment = {
17356     'left' : ['r-l', [-10,0], 'right'],
17357     'right' : ['l-r', [10,0], 'left'],
17358     'bottom' : ['t-b', [0,10], 'top'],
17359     'top' : [ 'b-t', [0,-10], 'bottom']
17360 };
17361
17362  /*
17363  * - LGPL
17364  *
17365  * Progress
17366  * 
17367  */
17368
17369 /**
17370  * @class Roo.bootstrap.Progress
17371  * @extends Roo.bootstrap.Component
17372  * Bootstrap Progress class
17373  * @cfg {Boolean} striped striped of the progress bar
17374  * @cfg {Boolean} active animated of the progress bar
17375  * 
17376  * 
17377  * @constructor
17378  * Create a new Progress
17379  * @param {Object} config The config object
17380  */
17381
17382 Roo.bootstrap.Progress = function(config){
17383     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17384 };
17385
17386 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17387     
17388     striped : false,
17389     active: false,
17390     
17391     getAutoCreate : function(){
17392         var cfg = {
17393             tag: 'div',
17394             cls: 'progress'
17395         };
17396         
17397         
17398         if(this.striped){
17399             cfg.cls += ' progress-striped';
17400         }
17401       
17402         if(this.active){
17403             cfg.cls += ' active';
17404         }
17405         
17406         
17407         return cfg;
17408     }
17409    
17410 });
17411
17412  
17413
17414  /*
17415  * - LGPL
17416  *
17417  * ProgressBar
17418  * 
17419  */
17420
17421 /**
17422  * @class Roo.bootstrap.ProgressBar
17423  * @extends Roo.bootstrap.Component
17424  * Bootstrap ProgressBar class
17425  * @cfg {Number} aria_valuenow aria-value now
17426  * @cfg {Number} aria_valuemin aria-value min
17427  * @cfg {Number} aria_valuemax aria-value max
17428  * @cfg {String} label label for the progress bar
17429  * @cfg {String} panel (success | info | warning | danger )
17430  * @cfg {String} role role of the progress bar
17431  * @cfg {String} sr_only text
17432  * 
17433  * 
17434  * @constructor
17435  * Create a new ProgressBar
17436  * @param {Object} config The config object
17437  */
17438
17439 Roo.bootstrap.ProgressBar = function(config){
17440     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17441 };
17442
17443 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17444     
17445     aria_valuenow : 0,
17446     aria_valuemin : 0,
17447     aria_valuemax : 100,
17448     label : false,
17449     panel : false,
17450     role : false,
17451     sr_only: false,
17452     
17453     getAutoCreate : function()
17454     {
17455         
17456         var cfg = {
17457             tag: 'div',
17458             cls: 'progress-bar',
17459             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17460         };
17461         
17462         if(this.sr_only){
17463             cfg.cn = {
17464                 tag: 'span',
17465                 cls: 'sr-only',
17466                 html: this.sr_only
17467             }
17468         }
17469         
17470         if(this.role){
17471             cfg.role = this.role;
17472         }
17473         
17474         if(this.aria_valuenow){
17475             cfg['aria-valuenow'] = this.aria_valuenow;
17476         }
17477         
17478         if(this.aria_valuemin){
17479             cfg['aria-valuemin'] = this.aria_valuemin;
17480         }
17481         
17482         if(this.aria_valuemax){
17483             cfg['aria-valuemax'] = this.aria_valuemax;
17484         }
17485         
17486         if(this.label && !this.sr_only){
17487             cfg.html = this.label;
17488         }
17489         
17490         if(this.panel){
17491             cfg.cls += ' progress-bar-' + this.panel;
17492         }
17493         
17494         return cfg;
17495     },
17496     
17497     update : function(aria_valuenow)
17498     {
17499         this.aria_valuenow = aria_valuenow;
17500         
17501         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17502     }
17503    
17504 });
17505
17506  
17507
17508  /*
17509  * - LGPL
17510  *
17511  * column
17512  * 
17513  */
17514
17515 /**
17516  * @class Roo.bootstrap.TabGroup
17517  * @extends Roo.bootstrap.Column
17518  * Bootstrap Column class
17519  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17520  * @cfg {Boolean} carousel true to make the group behave like a carousel
17521  * @cfg {Boolean} bullets show bullets for the panels
17522  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17523  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17524  * @cfg {Boolean} showarrow (true|false) show arrow default true
17525  * 
17526  * @constructor
17527  * Create a new TabGroup
17528  * @param {Object} config The config object
17529  */
17530
17531 Roo.bootstrap.TabGroup = function(config){
17532     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17533     if (!this.navId) {
17534         this.navId = Roo.id();
17535     }
17536     this.tabs = [];
17537     Roo.bootstrap.TabGroup.register(this);
17538     
17539 };
17540
17541 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17542     
17543     carousel : false,
17544     transition : false,
17545     bullets : 0,
17546     timer : 0,
17547     autoslide : false,
17548     slideFn : false,
17549     slideOnTouch : false,
17550     showarrow : true,
17551     
17552     getAutoCreate : function()
17553     {
17554         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17555         
17556         cfg.cls += ' tab-content';
17557         
17558         if (this.carousel) {
17559             cfg.cls += ' carousel slide';
17560             
17561             cfg.cn = [{
17562                cls : 'carousel-inner',
17563                cn : []
17564             }];
17565         
17566             if(this.bullets  && !Roo.isTouch){
17567                 
17568                 var bullets = {
17569                     cls : 'carousel-bullets',
17570                     cn : []
17571                 };
17572                
17573                 if(this.bullets_cls){
17574                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17575                 }
17576                 
17577                 bullets.cn.push({
17578                     cls : 'clear'
17579                 });
17580                 
17581                 cfg.cn[0].cn.push(bullets);
17582             }
17583             
17584             if(this.showarrow){
17585                 cfg.cn[0].cn.push({
17586                     tag : 'div',
17587                     class : 'carousel-arrow',
17588                     cn : [
17589                         {
17590                             tag : 'div',
17591                             class : 'carousel-prev',
17592                             cn : [
17593                                 {
17594                                     tag : 'i',
17595                                     class : 'fa fa-chevron-left'
17596                                 }
17597                             ]
17598                         },
17599                         {
17600                             tag : 'div',
17601                             class : 'carousel-next',
17602                             cn : [
17603                                 {
17604                                     tag : 'i',
17605                                     class : 'fa fa-chevron-right'
17606                                 }
17607                             ]
17608                         }
17609                     ]
17610                 });
17611             }
17612             
17613         }
17614         
17615         return cfg;
17616     },
17617     
17618     initEvents:  function()
17619     {
17620 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17621 //            this.el.on("touchstart", this.onTouchStart, this);
17622 //        }
17623         
17624         if(this.autoslide){
17625             var _this = this;
17626             
17627             this.slideFn = window.setInterval(function() {
17628                 _this.showPanelNext();
17629             }, this.timer);
17630         }
17631         
17632         if(this.showarrow){
17633             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17634             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17635         }
17636         
17637         
17638     },
17639     
17640 //    onTouchStart : function(e, el, o)
17641 //    {
17642 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17643 //            return;
17644 //        }
17645 //        
17646 //        this.showPanelNext();
17647 //    },
17648     
17649     
17650     getChildContainer : function()
17651     {
17652         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17653     },
17654     
17655     /**
17656     * register a Navigation item
17657     * @param {Roo.bootstrap.NavItem} the navitem to add
17658     */
17659     register : function(item)
17660     {
17661         this.tabs.push( item);
17662         item.navId = this.navId; // not really needed..
17663         this.addBullet();
17664     
17665     },
17666     
17667     getActivePanel : function()
17668     {
17669         var r = false;
17670         Roo.each(this.tabs, function(t) {
17671             if (t.active) {
17672                 r = t;
17673                 return false;
17674             }
17675             return null;
17676         });
17677         return r;
17678         
17679     },
17680     getPanelByName : function(n)
17681     {
17682         var r = false;
17683         Roo.each(this.tabs, function(t) {
17684             if (t.tabId == n) {
17685                 r = t;
17686                 return false;
17687             }
17688             return null;
17689         });
17690         return r;
17691     },
17692     indexOfPanel : function(p)
17693     {
17694         var r = false;
17695         Roo.each(this.tabs, function(t,i) {
17696             if (t.tabId == p.tabId) {
17697                 r = i;
17698                 return false;
17699             }
17700             return null;
17701         });
17702         return r;
17703     },
17704     /**
17705      * show a specific panel
17706      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17707      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17708      */
17709     showPanel : function (pan)
17710     {
17711         if(this.transition || typeof(pan) == 'undefined'){
17712             Roo.log("waiting for the transitionend");
17713             return;
17714         }
17715         
17716         if (typeof(pan) == 'number') {
17717             pan = this.tabs[pan];
17718         }
17719         
17720         if (typeof(pan) == 'string') {
17721             pan = this.getPanelByName(pan);
17722         }
17723         
17724         var cur = this.getActivePanel();
17725         
17726         if(!pan || !cur){
17727             Roo.log('pan or acitve pan is undefined');
17728             return false;
17729         }
17730         
17731         if (pan.tabId == this.getActivePanel().tabId) {
17732             return true;
17733         }
17734         
17735         if (false === cur.fireEvent('beforedeactivate')) {
17736             return false;
17737         }
17738         
17739         if(this.bullets > 0 && !Roo.isTouch){
17740             this.setActiveBullet(this.indexOfPanel(pan));
17741         }
17742         
17743         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17744             
17745             this.transition = true;
17746             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17747             var lr = dir == 'next' ? 'left' : 'right';
17748             pan.el.addClass(dir); // or prev
17749             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17750             cur.el.addClass(lr); // or right
17751             pan.el.addClass(lr);
17752             
17753             var _this = this;
17754             cur.el.on('transitionend', function() {
17755                 Roo.log("trans end?");
17756                 
17757                 pan.el.removeClass([lr,dir]);
17758                 pan.setActive(true);
17759                 
17760                 cur.el.removeClass([lr]);
17761                 cur.setActive(false);
17762                 
17763                 _this.transition = false;
17764                 
17765             }, this, { single:  true } );
17766             
17767             return true;
17768         }
17769         
17770         cur.setActive(false);
17771         pan.setActive(true);
17772         
17773         return true;
17774         
17775     },
17776     showPanelNext : function()
17777     {
17778         var i = this.indexOfPanel(this.getActivePanel());
17779         
17780         if (i >= this.tabs.length - 1 && !this.autoslide) {
17781             return;
17782         }
17783         
17784         if (i >= this.tabs.length - 1 && this.autoslide) {
17785             i = -1;
17786         }
17787         
17788         this.showPanel(this.tabs[i+1]);
17789     },
17790     
17791     showPanelPrev : function()
17792     {
17793         var i = this.indexOfPanel(this.getActivePanel());
17794         
17795         if (i  < 1 && !this.autoslide) {
17796             return;
17797         }
17798         
17799         if (i < 1 && this.autoslide) {
17800             i = this.tabs.length;
17801         }
17802         
17803         this.showPanel(this.tabs[i-1]);
17804     },
17805     
17806     
17807     addBullet: function()
17808     {
17809         if(!this.bullets || Roo.isTouch){
17810             return;
17811         }
17812         var ctr = this.el.select('.carousel-bullets',true).first();
17813         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17814         var bullet = ctr.createChild({
17815             cls : 'bullet bullet-' + i
17816         },ctr.dom.lastChild);
17817         
17818         
17819         var _this = this;
17820         
17821         bullet.on('click', (function(e, el, o, ii, t){
17822
17823             e.preventDefault();
17824
17825             this.showPanel(ii);
17826
17827             if(this.autoslide && this.slideFn){
17828                 clearInterval(this.slideFn);
17829                 this.slideFn = window.setInterval(function() {
17830                     _this.showPanelNext();
17831                 }, this.timer);
17832             }
17833
17834         }).createDelegate(this, [i, bullet], true));
17835                 
17836         
17837     },
17838      
17839     setActiveBullet : function(i)
17840     {
17841         if(Roo.isTouch){
17842             return;
17843         }
17844         
17845         Roo.each(this.el.select('.bullet', true).elements, function(el){
17846             el.removeClass('selected');
17847         });
17848
17849         var bullet = this.el.select('.bullet-' + i, true).first();
17850         
17851         if(!bullet){
17852             return;
17853         }
17854         
17855         bullet.addClass('selected');
17856     }
17857     
17858     
17859   
17860 });
17861
17862  
17863
17864  
17865  
17866 Roo.apply(Roo.bootstrap.TabGroup, {
17867     
17868     groups: {},
17869      /**
17870     * register a Navigation Group
17871     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17872     */
17873     register : function(navgrp)
17874     {
17875         this.groups[navgrp.navId] = navgrp;
17876         
17877     },
17878     /**
17879     * fetch a Navigation Group based on the navigation ID
17880     * if one does not exist , it will get created.
17881     * @param {string} the navgroup to add
17882     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17883     */
17884     get: function(navId) {
17885         if (typeof(this.groups[navId]) == 'undefined') {
17886             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17887         }
17888         return this.groups[navId] ;
17889     }
17890     
17891     
17892     
17893 });
17894
17895  /*
17896  * - LGPL
17897  *
17898  * TabPanel
17899  * 
17900  */
17901
17902 /**
17903  * @class Roo.bootstrap.TabPanel
17904  * @extends Roo.bootstrap.Component
17905  * Bootstrap TabPanel class
17906  * @cfg {Boolean} active panel active
17907  * @cfg {String} html panel content
17908  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17909  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17910  * @cfg {String} href click to link..
17911  * 
17912  * 
17913  * @constructor
17914  * Create a new TabPanel
17915  * @param {Object} config The config object
17916  */
17917
17918 Roo.bootstrap.TabPanel = function(config){
17919     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17920     this.addEvents({
17921         /**
17922              * @event changed
17923              * Fires when the active status changes
17924              * @param {Roo.bootstrap.TabPanel} this
17925              * @param {Boolean} state the new state
17926             
17927          */
17928         'changed': true,
17929         /**
17930              * @event beforedeactivate
17931              * Fires before a tab is de-activated - can be used to do validation on a form.
17932              * @param {Roo.bootstrap.TabPanel} this
17933              * @return {Boolean} false if there is an error
17934             
17935          */
17936         'beforedeactivate': true
17937      });
17938     
17939     this.tabId = this.tabId || Roo.id();
17940   
17941 };
17942
17943 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17944     
17945     active: false,
17946     html: false,
17947     tabId: false,
17948     navId : false,
17949     href : '',
17950     
17951     getAutoCreate : function(){
17952         var cfg = {
17953             tag: 'div',
17954             // item is needed for carousel - not sure if it has any effect otherwise
17955             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17956             html: this.html || ''
17957         };
17958         
17959         if(this.active){
17960             cfg.cls += ' active';
17961         }
17962         
17963         if(this.tabId){
17964             cfg.tabId = this.tabId;
17965         }
17966         
17967         
17968         return cfg;
17969     },
17970     
17971     initEvents:  function()
17972     {
17973         var p = this.parent();
17974         
17975         this.navId = this.navId || p.navId;
17976         
17977         if (typeof(this.navId) != 'undefined') {
17978             // not really needed.. but just in case.. parent should be a NavGroup.
17979             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17980             
17981             tg.register(this);
17982             
17983             var i = tg.tabs.length - 1;
17984             
17985             if(this.active && tg.bullets > 0 && i < tg.bullets){
17986                 tg.setActiveBullet(i);
17987             }
17988         }
17989         
17990         this.el.on('click', this.onClick, this);
17991         
17992         if(Roo.isTouch){
17993             this.el.on("touchstart", this.onTouchStart, this);
17994             this.el.on("touchmove", this.onTouchMove, this);
17995             this.el.on("touchend", this.onTouchEnd, this);
17996         }
17997         
17998     },
17999     
18000     onRender : function(ct, position)
18001     {
18002         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18003     },
18004     
18005     setActive : function(state)
18006     {
18007         Roo.log("panel - set active " + this.tabId + "=" + state);
18008         
18009         this.active = state;
18010         if (!state) {
18011             this.el.removeClass('active');
18012             
18013         } else  if (!this.el.hasClass('active')) {
18014             this.el.addClass('active');
18015         }
18016         
18017         this.fireEvent('changed', this, state);
18018     },
18019     
18020     onClick : function(e)
18021     {
18022         e.preventDefault();
18023         
18024         if(!this.href.length){
18025             return;
18026         }
18027         
18028         window.location.href = this.href;
18029     },
18030     
18031     startX : 0,
18032     startY : 0,
18033     endX : 0,
18034     endY : 0,
18035     swiping : false,
18036     
18037     onTouchStart : function(e)
18038     {
18039         this.swiping = false;
18040         
18041         this.startX = e.browserEvent.touches[0].clientX;
18042         this.startY = e.browserEvent.touches[0].clientY;
18043     },
18044     
18045     onTouchMove : function(e)
18046     {
18047         this.swiping = true;
18048         
18049         this.endX = e.browserEvent.touches[0].clientX;
18050         this.endY = e.browserEvent.touches[0].clientY;
18051     },
18052     
18053     onTouchEnd : function(e)
18054     {
18055         if(!this.swiping){
18056             this.onClick(e);
18057             return;
18058         }
18059         
18060         var tabGroup = this.parent();
18061         
18062         if(this.endX > this.startX){ // swiping right
18063             tabGroup.showPanelPrev();
18064             return;
18065         }
18066         
18067         if(this.startX > this.endX){ // swiping left
18068             tabGroup.showPanelNext();
18069             return;
18070         }
18071     }
18072     
18073     
18074 });
18075  
18076
18077  
18078
18079  /*
18080  * - LGPL
18081  *
18082  * DateField
18083  * 
18084  */
18085
18086 /**
18087  * @class Roo.bootstrap.DateField
18088  * @extends Roo.bootstrap.Input
18089  * Bootstrap DateField class
18090  * @cfg {Number} weekStart default 0
18091  * @cfg {String} viewMode default empty, (months|years)
18092  * @cfg {String} minViewMode default empty, (months|years)
18093  * @cfg {Number} startDate default -Infinity
18094  * @cfg {Number} endDate default Infinity
18095  * @cfg {Boolean} todayHighlight default false
18096  * @cfg {Boolean} todayBtn default false
18097  * @cfg {Boolean} calendarWeeks default false
18098  * @cfg {Object} daysOfWeekDisabled default empty
18099  * @cfg {Boolean} singleMode default false (true | false)
18100  * 
18101  * @cfg {Boolean} keyboardNavigation default true
18102  * @cfg {String} language default en
18103  * 
18104  * @constructor
18105  * Create a new DateField
18106  * @param {Object} config The config object
18107  */
18108
18109 Roo.bootstrap.DateField = function(config){
18110     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18111      this.addEvents({
18112             /**
18113              * @event show
18114              * Fires when this field show.
18115              * @param {Roo.bootstrap.DateField} this
18116              * @param {Mixed} date The date value
18117              */
18118             show : true,
18119             /**
18120              * @event show
18121              * Fires when this field hide.
18122              * @param {Roo.bootstrap.DateField} this
18123              * @param {Mixed} date The date value
18124              */
18125             hide : true,
18126             /**
18127              * @event select
18128              * Fires when select a date.
18129              * @param {Roo.bootstrap.DateField} this
18130              * @param {Mixed} date The date value
18131              */
18132             select : true,
18133             /**
18134              * @event beforeselect
18135              * Fires when before select a date.
18136              * @param {Roo.bootstrap.DateField} this
18137              * @param {Mixed} date The date value
18138              */
18139             beforeselect : true
18140         });
18141 };
18142
18143 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18144     
18145     /**
18146      * @cfg {String} format
18147      * The default date format string which can be overriden for localization support.  The format must be
18148      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18149      */
18150     format : "m/d/y",
18151     /**
18152      * @cfg {String} altFormats
18153      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18154      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18155      */
18156     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18157     
18158     weekStart : 0,
18159     
18160     viewMode : '',
18161     
18162     minViewMode : '',
18163     
18164     todayHighlight : false,
18165     
18166     todayBtn: false,
18167     
18168     language: 'en',
18169     
18170     keyboardNavigation: true,
18171     
18172     calendarWeeks: false,
18173     
18174     startDate: -Infinity,
18175     
18176     endDate: Infinity,
18177     
18178     daysOfWeekDisabled: [],
18179     
18180     _events: [],
18181     
18182     singleMode : false,
18183     
18184     UTCDate: function()
18185     {
18186         return new Date(Date.UTC.apply(Date, arguments));
18187     },
18188     
18189     UTCToday: function()
18190     {
18191         var today = new Date();
18192         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18193     },
18194     
18195     getDate: function() {
18196             var d = this.getUTCDate();
18197             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18198     },
18199     
18200     getUTCDate: function() {
18201             return this.date;
18202     },
18203     
18204     setDate: function(d) {
18205             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18206     },
18207     
18208     setUTCDate: function(d) {
18209             this.date = d;
18210             this.setValue(this.formatDate(this.date));
18211     },
18212         
18213     onRender: function(ct, position)
18214     {
18215         
18216         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18217         
18218         this.language = this.language || 'en';
18219         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18220         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18221         
18222         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18223         this.format = this.format || 'm/d/y';
18224         this.isInline = false;
18225         this.isInput = true;
18226         this.component = this.el.select('.add-on', true).first() || false;
18227         this.component = (this.component && this.component.length === 0) ? false : this.component;
18228         this.hasInput = this.component && this.inputEl().length;
18229         
18230         if (typeof(this.minViewMode === 'string')) {
18231             switch (this.minViewMode) {
18232                 case 'months':
18233                     this.minViewMode = 1;
18234                     break;
18235                 case 'years':
18236                     this.minViewMode = 2;
18237                     break;
18238                 default:
18239                     this.minViewMode = 0;
18240                     break;
18241             }
18242         }
18243         
18244         if (typeof(this.viewMode === 'string')) {
18245             switch (this.viewMode) {
18246                 case 'months':
18247                     this.viewMode = 1;
18248                     break;
18249                 case 'years':
18250                     this.viewMode = 2;
18251                     break;
18252                 default:
18253                     this.viewMode = 0;
18254                     break;
18255             }
18256         }
18257                 
18258         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18259         
18260 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18261         
18262         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18263         
18264         this.picker().on('mousedown', this.onMousedown, this);
18265         this.picker().on('click', this.onClick, this);
18266         
18267         this.picker().addClass('datepicker-dropdown');
18268         
18269         this.startViewMode = this.viewMode;
18270         
18271         if(this.singleMode){
18272             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18273                 v.setVisibilityMode(Roo.Element.DISPLAY);
18274                 v.hide();
18275             });
18276             
18277             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18278                 v.setStyle('width', '189px');
18279             });
18280         }
18281         
18282         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18283             if(!this.calendarWeeks){
18284                 v.remove();
18285                 return;
18286             }
18287             
18288             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18289             v.attr('colspan', function(i, val){
18290                 return parseInt(val) + 1;
18291             });
18292         });
18293                         
18294         
18295         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18296         
18297         this.setStartDate(this.startDate);
18298         this.setEndDate(this.endDate);
18299         
18300         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18301         
18302         this.fillDow();
18303         this.fillMonths();
18304         this.update();
18305         this.showMode();
18306         
18307         if(this.isInline) {
18308             this.show();
18309         }
18310     },
18311     
18312     picker : function()
18313     {
18314         return this.pickerEl;
18315 //        return this.el.select('.datepicker', true).first();
18316     },
18317     
18318     fillDow: function()
18319     {
18320         var dowCnt = this.weekStart;
18321         
18322         var dow = {
18323             tag: 'tr',
18324             cn: [
18325                 
18326             ]
18327         };
18328         
18329         if(this.calendarWeeks){
18330             dow.cn.push({
18331                 tag: 'th',
18332                 cls: 'cw',
18333                 html: '&nbsp;'
18334             })
18335         }
18336         
18337         while (dowCnt < this.weekStart + 7) {
18338             dow.cn.push({
18339                 tag: 'th',
18340                 cls: 'dow',
18341                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18342             });
18343         }
18344         
18345         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18346     },
18347     
18348     fillMonths: function()
18349     {    
18350         var i = 0;
18351         var months = this.picker().select('>.datepicker-months td', true).first();
18352         
18353         months.dom.innerHTML = '';
18354         
18355         while (i < 12) {
18356             var month = {
18357                 tag: 'span',
18358                 cls: 'month',
18359                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18360             };
18361             
18362             months.createChild(month);
18363         }
18364         
18365     },
18366     
18367     update: function()
18368     {
18369         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;
18370         
18371         if (this.date < this.startDate) {
18372             this.viewDate = new Date(this.startDate);
18373         } else if (this.date > this.endDate) {
18374             this.viewDate = new Date(this.endDate);
18375         } else {
18376             this.viewDate = new Date(this.date);
18377         }
18378         
18379         this.fill();
18380     },
18381     
18382     fill: function() 
18383     {
18384         var d = new Date(this.viewDate),
18385                 year = d.getUTCFullYear(),
18386                 month = d.getUTCMonth(),
18387                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18388                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18389                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18390                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18391                 currentDate = this.date && this.date.valueOf(),
18392                 today = this.UTCToday();
18393         
18394         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18395         
18396 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18397         
18398 //        this.picker.select('>tfoot th.today').
18399 //                                              .text(dates[this.language].today)
18400 //                                              .toggle(this.todayBtn !== false);
18401     
18402         this.updateNavArrows();
18403         this.fillMonths();
18404                                                 
18405         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18406         
18407         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18408          
18409         prevMonth.setUTCDate(day);
18410         
18411         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18412         
18413         var nextMonth = new Date(prevMonth);
18414         
18415         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18416         
18417         nextMonth = nextMonth.valueOf();
18418         
18419         var fillMonths = false;
18420         
18421         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18422         
18423         while(prevMonth.valueOf() < nextMonth) {
18424             var clsName = '';
18425             
18426             if (prevMonth.getUTCDay() === this.weekStart) {
18427                 if(fillMonths){
18428                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18429                 }
18430                     
18431                 fillMonths = {
18432                     tag: 'tr',
18433                     cn: []
18434                 };
18435                 
18436                 if(this.calendarWeeks){
18437                     // ISO 8601: First week contains first thursday.
18438                     // ISO also states week starts on Monday, but we can be more abstract here.
18439                     var
18440                     // Start of current week: based on weekstart/current date
18441                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18442                     // Thursday of this week
18443                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18444                     // First Thursday of year, year from thursday
18445                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18446                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18447                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18448                     
18449                     fillMonths.cn.push({
18450                         tag: 'td',
18451                         cls: 'cw',
18452                         html: calWeek
18453                     });
18454                 }
18455             }
18456             
18457             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18458                 clsName += ' old';
18459             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18460                 clsName += ' new';
18461             }
18462             if (this.todayHighlight &&
18463                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18464                 prevMonth.getUTCMonth() == today.getMonth() &&
18465                 prevMonth.getUTCDate() == today.getDate()) {
18466                 clsName += ' today';
18467             }
18468             
18469             if (currentDate && prevMonth.valueOf() === currentDate) {
18470                 clsName += ' active';
18471             }
18472             
18473             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18474                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18475                     clsName += ' disabled';
18476             }
18477             
18478             fillMonths.cn.push({
18479                 tag: 'td',
18480                 cls: 'day ' + clsName,
18481                 html: prevMonth.getDate()
18482             });
18483             
18484             prevMonth.setDate(prevMonth.getDate()+1);
18485         }
18486           
18487         var currentYear = this.date && this.date.getUTCFullYear();
18488         var currentMonth = this.date && this.date.getUTCMonth();
18489         
18490         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18491         
18492         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18493             v.removeClass('active');
18494             
18495             if(currentYear === year && k === currentMonth){
18496                 v.addClass('active');
18497             }
18498             
18499             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18500                 v.addClass('disabled');
18501             }
18502             
18503         });
18504         
18505         
18506         year = parseInt(year/10, 10) * 10;
18507         
18508         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18509         
18510         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18511         
18512         year -= 1;
18513         for (var i = -1; i < 11; i++) {
18514             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18515                 tag: 'span',
18516                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18517                 html: year
18518             });
18519             
18520             year += 1;
18521         }
18522     },
18523     
18524     showMode: function(dir) 
18525     {
18526         if (dir) {
18527             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18528         }
18529         
18530         Roo.each(this.picker().select('>div',true).elements, function(v){
18531             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18532             v.hide();
18533         });
18534         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18535     },
18536     
18537     place: function()
18538     {
18539         if(this.isInline) {
18540             return;
18541         }
18542         
18543         this.picker().removeClass(['bottom', 'top']);
18544         
18545         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18546             /*
18547              * place to the top of element!
18548              *
18549              */
18550             
18551             this.picker().addClass('top');
18552             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18553             
18554             return;
18555         }
18556         
18557         this.picker().addClass('bottom');
18558         
18559         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18560     },
18561     
18562     parseDate : function(value)
18563     {
18564         if(!value || value instanceof Date){
18565             return value;
18566         }
18567         var v = Date.parseDate(value, this.format);
18568         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18569             v = Date.parseDate(value, 'Y-m-d');
18570         }
18571         if(!v && this.altFormats){
18572             if(!this.altFormatsArray){
18573                 this.altFormatsArray = this.altFormats.split("|");
18574             }
18575             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18576                 v = Date.parseDate(value, this.altFormatsArray[i]);
18577             }
18578         }
18579         return v;
18580     },
18581     
18582     formatDate : function(date, fmt)
18583     {   
18584         return (!date || !(date instanceof Date)) ?
18585         date : date.dateFormat(fmt || this.format);
18586     },
18587     
18588     onFocus : function()
18589     {
18590         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18591         this.show();
18592     },
18593     
18594     onBlur : function()
18595     {
18596         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18597         
18598         var d = this.inputEl().getValue();
18599         
18600         this.setValue(d);
18601                 
18602         this.hide();
18603     },
18604     
18605     show : function()
18606     {
18607         this.picker().show();
18608         this.update();
18609         this.place();
18610         
18611         this.fireEvent('show', this, this.date);
18612     },
18613     
18614     hide : function()
18615     {
18616         if(this.isInline) {
18617             return;
18618         }
18619         this.picker().hide();
18620         this.viewMode = this.startViewMode;
18621         this.showMode();
18622         
18623         this.fireEvent('hide', this, this.date);
18624         
18625     },
18626     
18627     onMousedown: function(e)
18628     {
18629         e.stopPropagation();
18630         e.preventDefault();
18631     },
18632     
18633     keyup: function(e)
18634     {
18635         Roo.bootstrap.DateField.superclass.keyup.call(this);
18636         this.update();
18637     },
18638
18639     setValue: function(v)
18640     {
18641         if(this.fireEvent('beforeselect', this, v) !== false){
18642             var d = new Date(this.parseDate(v) ).clearTime();
18643         
18644             if(isNaN(d.getTime())){
18645                 this.date = this.viewDate = '';
18646                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18647                 return;
18648             }
18649
18650             v = this.formatDate(d);
18651
18652             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18653
18654             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18655
18656             this.update();
18657
18658             this.fireEvent('select', this, this.date);
18659         }
18660     },
18661     
18662     getValue: function()
18663     {
18664         return this.formatDate(this.date);
18665     },
18666     
18667     fireKey: function(e)
18668     {
18669         if (!this.picker().isVisible()){
18670             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18671                 this.show();
18672             }
18673             return;
18674         }
18675         
18676         var dateChanged = false,
18677         dir, day, month,
18678         newDate, newViewDate;
18679         
18680         switch(e.keyCode){
18681             case 27: // escape
18682                 this.hide();
18683                 e.preventDefault();
18684                 break;
18685             case 37: // left
18686             case 39: // right
18687                 if (!this.keyboardNavigation) {
18688                     break;
18689                 }
18690                 dir = e.keyCode == 37 ? -1 : 1;
18691                 
18692                 if (e.ctrlKey){
18693                     newDate = this.moveYear(this.date, dir);
18694                     newViewDate = this.moveYear(this.viewDate, dir);
18695                 } else if (e.shiftKey){
18696                     newDate = this.moveMonth(this.date, dir);
18697                     newViewDate = this.moveMonth(this.viewDate, dir);
18698                 } else {
18699                     newDate = new Date(this.date);
18700                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18701                     newViewDate = new Date(this.viewDate);
18702                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18703                 }
18704                 if (this.dateWithinRange(newDate)){
18705                     this.date = newDate;
18706                     this.viewDate = newViewDate;
18707                     this.setValue(this.formatDate(this.date));
18708 //                    this.update();
18709                     e.preventDefault();
18710                     dateChanged = true;
18711                 }
18712                 break;
18713             case 38: // up
18714             case 40: // down
18715                 if (!this.keyboardNavigation) {
18716                     break;
18717                 }
18718                 dir = e.keyCode == 38 ? -1 : 1;
18719                 if (e.ctrlKey){
18720                     newDate = this.moveYear(this.date, dir);
18721                     newViewDate = this.moveYear(this.viewDate, dir);
18722                 } else if (e.shiftKey){
18723                     newDate = this.moveMonth(this.date, dir);
18724                     newViewDate = this.moveMonth(this.viewDate, dir);
18725                 } else {
18726                     newDate = new Date(this.date);
18727                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18728                     newViewDate = new Date(this.viewDate);
18729                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18730                 }
18731                 if (this.dateWithinRange(newDate)){
18732                     this.date = newDate;
18733                     this.viewDate = newViewDate;
18734                     this.setValue(this.formatDate(this.date));
18735 //                    this.update();
18736                     e.preventDefault();
18737                     dateChanged = true;
18738                 }
18739                 break;
18740             case 13: // enter
18741                 this.setValue(this.formatDate(this.date));
18742                 this.hide();
18743                 e.preventDefault();
18744                 break;
18745             case 9: // tab
18746                 this.setValue(this.formatDate(this.date));
18747                 this.hide();
18748                 break;
18749             case 16: // shift
18750             case 17: // ctrl
18751             case 18: // alt
18752                 break;
18753             default :
18754                 this.hide();
18755                 
18756         }
18757     },
18758     
18759     
18760     onClick: function(e) 
18761     {
18762         e.stopPropagation();
18763         e.preventDefault();
18764         
18765         var target = e.getTarget();
18766         
18767         if(target.nodeName.toLowerCase() === 'i'){
18768             target = Roo.get(target).dom.parentNode;
18769         }
18770         
18771         var nodeName = target.nodeName;
18772         var className = target.className;
18773         var html = target.innerHTML;
18774         //Roo.log(nodeName);
18775         
18776         switch(nodeName.toLowerCase()) {
18777             case 'th':
18778                 switch(className) {
18779                     case 'switch':
18780                         this.showMode(1);
18781                         break;
18782                     case 'prev':
18783                     case 'next':
18784                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18785                         switch(this.viewMode){
18786                                 case 0:
18787                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18788                                         break;
18789                                 case 1:
18790                                 case 2:
18791                                         this.viewDate = this.moveYear(this.viewDate, dir);
18792                                         break;
18793                         }
18794                         this.fill();
18795                         break;
18796                     case 'today':
18797                         var date = new Date();
18798                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18799 //                        this.fill()
18800                         this.setValue(this.formatDate(this.date));
18801                         
18802                         this.hide();
18803                         break;
18804                 }
18805                 break;
18806             case 'span':
18807                 if (className.indexOf('disabled') < 0) {
18808                     this.viewDate.setUTCDate(1);
18809                     if (className.indexOf('month') > -1) {
18810                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18811                     } else {
18812                         var year = parseInt(html, 10) || 0;
18813                         this.viewDate.setUTCFullYear(year);
18814                         
18815                     }
18816                     
18817                     if(this.singleMode){
18818                         this.setValue(this.formatDate(this.viewDate));
18819                         this.hide();
18820                         return;
18821                     }
18822                     
18823                     this.showMode(-1);
18824                     this.fill();
18825                 }
18826                 break;
18827                 
18828             case 'td':
18829                 //Roo.log(className);
18830                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18831                     var day = parseInt(html, 10) || 1;
18832                     var year = this.viewDate.getUTCFullYear(),
18833                         month = this.viewDate.getUTCMonth();
18834
18835                     if (className.indexOf('old') > -1) {
18836                         if(month === 0 ){
18837                             month = 11;
18838                             year -= 1;
18839                         }else{
18840                             month -= 1;
18841                         }
18842                     } else if (className.indexOf('new') > -1) {
18843                         if (month == 11) {
18844                             month = 0;
18845                             year += 1;
18846                         } else {
18847                             month += 1;
18848                         }
18849                     }
18850                     //Roo.log([year,month,day]);
18851                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18852                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18853 //                    this.fill();
18854                     //Roo.log(this.formatDate(this.date));
18855                     this.setValue(this.formatDate(this.date));
18856                     this.hide();
18857                 }
18858                 break;
18859         }
18860     },
18861     
18862     setStartDate: function(startDate)
18863     {
18864         this.startDate = startDate || -Infinity;
18865         if (this.startDate !== -Infinity) {
18866             this.startDate = this.parseDate(this.startDate);
18867         }
18868         this.update();
18869         this.updateNavArrows();
18870     },
18871
18872     setEndDate: function(endDate)
18873     {
18874         this.endDate = endDate || Infinity;
18875         if (this.endDate !== Infinity) {
18876             this.endDate = this.parseDate(this.endDate);
18877         }
18878         this.update();
18879         this.updateNavArrows();
18880     },
18881     
18882     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18883     {
18884         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18885         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18886             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18887         }
18888         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18889             return parseInt(d, 10);
18890         });
18891         this.update();
18892         this.updateNavArrows();
18893     },
18894     
18895     updateNavArrows: function() 
18896     {
18897         if(this.singleMode){
18898             return;
18899         }
18900         
18901         var d = new Date(this.viewDate),
18902         year = d.getUTCFullYear(),
18903         month = d.getUTCMonth();
18904         
18905         Roo.each(this.picker().select('.prev', true).elements, function(v){
18906             v.show();
18907             switch (this.viewMode) {
18908                 case 0:
18909
18910                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18911                         v.hide();
18912                     }
18913                     break;
18914                 case 1:
18915                 case 2:
18916                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18917                         v.hide();
18918                     }
18919                     break;
18920             }
18921         });
18922         
18923         Roo.each(this.picker().select('.next', true).elements, function(v){
18924             v.show();
18925             switch (this.viewMode) {
18926                 case 0:
18927
18928                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18929                         v.hide();
18930                     }
18931                     break;
18932                 case 1:
18933                 case 2:
18934                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18935                         v.hide();
18936                     }
18937                     break;
18938             }
18939         })
18940     },
18941     
18942     moveMonth: function(date, dir)
18943     {
18944         if (!dir) {
18945             return date;
18946         }
18947         var new_date = new Date(date.valueOf()),
18948         day = new_date.getUTCDate(),
18949         month = new_date.getUTCMonth(),
18950         mag = Math.abs(dir),
18951         new_month, test;
18952         dir = dir > 0 ? 1 : -1;
18953         if (mag == 1){
18954             test = dir == -1
18955             // If going back one month, make sure month is not current month
18956             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18957             ? function(){
18958                 return new_date.getUTCMonth() == month;
18959             }
18960             // If going forward one month, make sure month is as expected
18961             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18962             : function(){
18963                 return new_date.getUTCMonth() != new_month;
18964             };
18965             new_month = month + dir;
18966             new_date.setUTCMonth(new_month);
18967             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18968             if (new_month < 0 || new_month > 11) {
18969                 new_month = (new_month + 12) % 12;
18970             }
18971         } else {
18972             // For magnitudes >1, move one month at a time...
18973             for (var i=0; i<mag; i++) {
18974                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18975                 new_date = this.moveMonth(new_date, dir);
18976             }
18977             // ...then reset the day, keeping it in the new month
18978             new_month = new_date.getUTCMonth();
18979             new_date.setUTCDate(day);
18980             test = function(){
18981                 return new_month != new_date.getUTCMonth();
18982             };
18983         }
18984         // Common date-resetting loop -- if date is beyond end of month, make it
18985         // end of month
18986         while (test()){
18987             new_date.setUTCDate(--day);
18988             new_date.setUTCMonth(new_month);
18989         }
18990         return new_date;
18991     },
18992
18993     moveYear: function(date, dir)
18994     {
18995         return this.moveMonth(date, dir*12);
18996     },
18997
18998     dateWithinRange: function(date)
18999     {
19000         return date >= this.startDate && date <= this.endDate;
19001     },
19002
19003     
19004     remove: function() 
19005     {
19006         this.picker().remove();
19007     },
19008     
19009     validateValue : function(value)
19010     {
19011         if(value.length < 1)  {
19012             if(this.allowBlank){
19013                 return true;
19014             }
19015             return false;
19016         }
19017         
19018         if(value.length < this.minLength){
19019             return false;
19020         }
19021         if(value.length > this.maxLength){
19022             return false;
19023         }
19024         if(this.vtype){
19025             var vt = Roo.form.VTypes;
19026             if(!vt[this.vtype](value, this)){
19027                 return false;
19028             }
19029         }
19030         if(typeof this.validator == "function"){
19031             var msg = this.validator(value);
19032             if(msg !== true){
19033                 return false;
19034             }
19035         }
19036         
19037         if(this.regex && !this.regex.test(value)){
19038             return false;
19039         }
19040         
19041         if(typeof(this.parseDate(value)) == 'undefined'){
19042             return false;
19043         }
19044         
19045         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19046             return false;
19047         }      
19048         
19049         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19050             return false;
19051         } 
19052         
19053         
19054         return true;
19055     }
19056    
19057 });
19058
19059 Roo.apply(Roo.bootstrap.DateField,  {
19060     
19061     head : {
19062         tag: 'thead',
19063         cn: [
19064         {
19065             tag: 'tr',
19066             cn: [
19067             {
19068                 tag: 'th',
19069                 cls: 'prev',
19070                 html: '<i class="fa fa-arrow-left"/>'
19071             },
19072             {
19073                 tag: 'th',
19074                 cls: 'switch',
19075                 colspan: '5'
19076             },
19077             {
19078                 tag: 'th',
19079                 cls: 'next',
19080                 html: '<i class="fa fa-arrow-right"/>'
19081             }
19082
19083             ]
19084         }
19085         ]
19086     },
19087     
19088     content : {
19089         tag: 'tbody',
19090         cn: [
19091         {
19092             tag: 'tr',
19093             cn: [
19094             {
19095                 tag: 'td',
19096                 colspan: '7'
19097             }
19098             ]
19099         }
19100         ]
19101     },
19102     
19103     footer : {
19104         tag: 'tfoot',
19105         cn: [
19106         {
19107             tag: 'tr',
19108             cn: [
19109             {
19110                 tag: 'th',
19111                 colspan: '7',
19112                 cls: 'today'
19113             }
19114                     
19115             ]
19116         }
19117         ]
19118     },
19119     
19120     dates:{
19121         en: {
19122             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19123             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19124             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19125             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19126             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19127             today: "Today"
19128         }
19129     },
19130     
19131     modes: [
19132     {
19133         clsName: 'days',
19134         navFnc: 'Month',
19135         navStep: 1
19136     },
19137     {
19138         clsName: 'months',
19139         navFnc: 'FullYear',
19140         navStep: 1
19141     },
19142     {
19143         clsName: 'years',
19144         navFnc: 'FullYear',
19145         navStep: 10
19146     }]
19147 });
19148
19149 Roo.apply(Roo.bootstrap.DateField,  {
19150   
19151     template : {
19152         tag: 'div',
19153         cls: 'datepicker dropdown-menu roo-dynamic',
19154         cn: [
19155         {
19156             tag: 'div',
19157             cls: 'datepicker-days',
19158             cn: [
19159             {
19160                 tag: 'table',
19161                 cls: 'table-condensed',
19162                 cn:[
19163                 Roo.bootstrap.DateField.head,
19164                 {
19165                     tag: 'tbody'
19166                 },
19167                 Roo.bootstrap.DateField.footer
19168                 ]
19169             }
19170             ]
19171         },
19172         {
19173             tag: 'div',
19174             cls: 'datepicker-months',
19175             cn: [
19176             {
19177                 tag: 'table',
19178                 cls: 'table-condensed',
19179                 cn:[
19180                 Roo.bootstrap.DateField.head,
19181                 Roo.bootstrap.DateField.content,
19182                 Roo.bootstrap.DateField.footer
19183                 ]
19184             }
19185             ]
19186         },
19187         {
19188             tag: 'div',
19189             cls: 'datepicker-years',
19190             cn: [
19191             {
19192                 tag: 'table',
19193                 cls: 'table-condensed',
19194                 cn:[
19195                 Roo.bootstrap.DateField.head,
19196                 Roo.bootstrap.DateField.content,
19197                 Roo.bootstrap.DateField.footer
19198                 ]
19199             }
19200             ]
19201         }
19202         ]
19203     }
19204 });
19205
19206  
19207
19208  /*
19209  * - LGPL
19210  *
19211  * TimeField
19212  * 
19213  */
19214
19215 /**
19216  * @class Roo.bootstrap.TimeField
19217  * @extends Roo.bootstrap.Input
19218  * Bootstrap DateField class
19219  * 
19220  * 
19221  * @constructor
19222  * Create a new TimeField
19223  * @param {Object} config The config object
19224  */
19225
19226 Roo.bootstrap.TimeField = function(config){
19227     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19228     this.addEvents({
19229             /**
19230              * @event show
19231              * Fires when this field show.
19232              * @param {Roo.bootstrap.DateField} thisthis
19233              * @param {Mixed} date The date value
19234              */
19235             show : true,
19236             /**
19237              * @event show
19238              * Fires when this field hide.
19239              * @param {Roo.bootstrap.DateField} this
19240              * @param {Mixed} date The date value
19241              */
19242             hide : true,
19243             /**
19244              * @event select
19245              * Fires when select a date.
19246              * @param {Roo.bootstrap.DateField} this
19247              * @param {Mixed} date The date value
19248              */
19249             select : true
19250         });
19251 };
19252
19253 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19254     
19255     /**
19256      * @cfg {String} format
19257      * The default time format string which can be overriden for localization support.  The format must be
19258      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19259      */
19260     format : "H:i",
19261        
19262     onRender: function(ct, position)
19263     {
19264         
19265         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19266                 
19267         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19268         
19269         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19270         
19271         this.pop = this.picker().select('>.datepicker-time',true).first();
19272         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19273         
19274         this.picker().on('mousedown', this.onMousedown, this);
19275         this.picker().on('click', this.onClick, this);
19276         
19277         this.picker().addClass('datepicker-dropdown');
19278     
19279         this.fillTime();
19280         this.update();
19281             
19282         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19283         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19284         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19285         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19286         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19287         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19288
19289     },
19290     
19291     fireKey: function(e){
19292         if (!this.picker().isVisible()){
19293             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19294                 this.show();
19295             }
19296             return;
19297         }
19298
19299         e.preventDefault();
19300         
19301         switch(e.keyCode){
19302             case 27: // escape
19303                 this.hide();
19304                 break;
19305             case 37: // left
19306             case 39: // right
19307                 this.onTogglePeriod();
19308                 break;
19309             case 38: // up
19310                 this.onIncrementMinutes();
19311                 break;
19312             case 40: // down
19313                 this.onDecrementMinutes();
19314                 break;
19315             case 13: // enter
19316             case 9: // tab
19317                 this.setTime();
19318                 break;
19319         }
19320     },
19321     
19322     onClick: function(e) {
19323         e.stopPropagation();
19324         e.preventDefault();
19325     },
19326     
19327     picker : function()
19328     {
19329         return this.el.select('.datepicker', true).first();
19330     },
19331     
19332     fillTime: function()
19333     {    
19334         var time = this.pop.select('tbody', true).first();
19335         
19336         time.dom.innerHTML = '';
19337         
19338         time.createChild({
19339             tag: 'tr',
19340             cn: [
19341                 {
19342                     tag: 'td',
19343                     cn: [
19344                         {
19345                             tag: 'a',
19346                             href: '#',
19347                             cls: 'btn',
19348                             cn: [
19349                                 {
19350                                     tag: 'span',
19351                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19352                                 }
19353                             ]
19354                         } 
19355                     ]
19356                 },
19357                 {
19358                     tag: 'td',
19359                     cls: 'separator'
19360                 },
19361                 {
19362                     tag: 'td',
19363                     cn: [
19364                         {
19365                             tag: 'a',
19366                             href: '#',
19367                             cls: 'btn',
19368                             cn: [
19369                                 {
19370                                     tag: 'span',
19371                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19372                                 }
19373                             ]
19374                         }
19375                     ]
19376                 },
19377                 {
19378                     tag: 'td',
19379                     cls: 'separator'
19380                 }
19381             ]
19382         });
19383         
19384         time.createChild({
19385             tag: 'tr',
19386             cn: [
19387                 {
19388                     tag: 'td',
19389                     cn: [
19390                         {
19391                             tag: 'span',
19392                             cls: 'timepicker-hour',
19393                             html: '00'
19394                         }  
19395                     ]
19396                 },
19397                 {
19398                     tag: 'td',
19399                     cls: 'separator',
19400                     html: ':'
19401                 },
19402                 {
19403                     tag: 'td',
19404                     cn: [
19405                         {
19406                             tag: 'span',
19407                             cls: 'timepicker-minute',
19408                             html: '00'
19409                         }  
19410                     ]
19411                 },
19412                 {
19413                     tag: 'td',
19414                     cls: 'separator'
19415                 },
19416                 {
19417                     tag: 'td',
19418                     cn: [
19419                         {
19420                             tag: 'button',
19421                             type: 'button',
19422                             cls: 'btn btn-primary period',
19423                             html: 'AM'
19424                             
19425                         }
19426                     ]
19427                 }
19428             ]
19429         });
19430         
19431         time.createChild({
19432             tag: 'tr',
19433             cn: [
19434                 {
19435                     tag: 'td',
19436                     cn: [
19437                         {
19438                             tag: 'a',
19439                             href: '#',
19440                             cls: 'btn',
19441                             cn: [
19442                                 {
19443                                     tag: 'span',
19444                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19445                                 }
19446                             ]
19447                         }
19448                     ]
19449                 },
19450                 {
19451                     tag: 'td',
19452                     cls: 'separator'
19453                 },
19454                 {
19455                     tag: 'td',
19456                     cn: [
19457                         {
19458                             tag: 'a',
19459                             href: '#',
19460                             cls: 'btn',
19461                             cn: [
19462                                 {
19463                                     tag: 'span',
19464                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19465                                 }
19466                             ]
19467                         }
19468                     ]
19469                 },
19470                 {
19471                     tag: 'td',
19472                     cls: 'separator'
19473                 }
19474             ]
19475         });
19476         
19477     },
19478     
19479     update: function()
19480     {
19481         
19482         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19483         
19484         this.fill();
19485     },
19486     
19487     fill: function() 
19488     {
19489         var hours = this.time.getHours();
19490         var minutes = this.time.getMinutes();
19491         var period = 'AM';
19492         
19493         if(hours > 11){
19494             period = 'PM';
19495         }
19496         
19497         if(hours == 0){
19498             hours = 12;
19499         }
19500         
19501         
19502         if(hours > 12){
19503             hours = hours - 12;
19504         }
19505         
19506         if(hours < 10){
19507             hours = '0' + hours;
19508         }
19509         
19510         if(minutes < 10){
19511             minutes = '0' + minutes;
19512         }
19513         
19514         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19515         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19516         this.pop.select('button', true).first().dom.innerHTML = period;
19517         
19518     },
19519     
19520     place: function()
19521     {   
19522         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19523         
19524         var cls = ['bottom'];
19525         
19526         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19527             cls.pop();
19528             cls.push('top');
19529         }
19530         
19531         cls.push('right');
19532         
19533         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19534             cls.pop();
19535             cls.push('left');
19536         }
19537         
19538         this.picker().addClass(cls.join('-'));
19539         
19540         var _this = this;
19541         
19542         Roo.each(cls, function(c){
19543             if(c == 'bottom'){
19544                 _this.picker().setTop(_this.inputEl().getHeight());
19545                 return;
19546             }
19547             if(c == 'top'){
19548                 _this.picker().setTop(0 - _this.picker().getHeight());
19549                 return;
19550             }
19551             
19552             if(c == 'left'){
19553                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19554                 return;
19555             }
19556             if(c == 'right'){
19557                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19558                 return;
19559             }
19560         });
19561         
19562     },
19563   
19564     onFocus : function()
19565     {
19566         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19567         this.show();
19568     },
19569     
19570     onBlur : function()
19571     {
19572         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19573         this.hide();
19574     },
19575     
19576     show : function()
19577     {
19578         this.picker().show();
19579         this.pop.show();
19580         this.update();
19581         this.place();
19582         
19583         this.fireEvent('show', this, this.date);
19584     },
19585     
19586     hide : function()
19587     {
19588         this.picker().hide();
19589         this.pop.hide();
19590         
19591         this.fireEvent('hide', this, this.date);
19592     },
19593     
19594     setTime : function()
19595     {
19596         this.hide();
19597         this.setValue(this.time.format(this.format));
19598         
19599         this.fireEvent('select', this, this.date);
19600         
19601         
19602     },
19603     
19604     onMousedown: function(e){
19605         e.stopPropagation();
19606         e.preventDefault();
19607     },
19608     
19609     onIncrementHours: function()
19610     {
19611         Roo.log('onIncrementHours');
19612         this.time = this.time.add(Date.HOUR, 1);
19613         this.update();
19614         
19615     },
19616     
19617     onDecrementHours: function()
19618     {
19619         Roo.log('onDecrementHours');
19620         this.time = this.time.add(Date.HOUR, -1);
19621         this.update();
19622     },
19623     
19624     onIncrementMinutes: function()
19625     {
19626         Roo.log('onIncrementMinutes');
19627         this.time = this.time.add(Date.MINUTE, 1);
19628         this.update();
19629     },
19630     
19631     onDecrementMinutes: function()
19632     {
19633         Roo.log('onDecrementMinutes');
19634         this.time = this.time.add(Date.MINUTE, -1);
19635         this.update();
19636     },
19637     
19638     onTogglePeriod: function()
19639     {
19640         Roo.log('onTogglePeriod');
19641         this.time = this.time.add(Date.HOUR, 12);
19642         this.update();
19643     }
19644     
19645    
19646 });
19647
19648 Roo.apply(Roo.bootstrap.TimeField,  {
19649     
19650     content : {
19651         tag: 'tbody',
19652         cn: [
19653             {
19654                 tag: 'tr',
19655                 cn: [
19656                 {
19657                     tag: 'td',
19658                     colspan: '7'
19659                 }
19660                 ]
19661             }
19662         ]
19663     },
19664     
19665     footer : {
19666         tag: 'tfoot',
19667         cn: [
19668             {
19669                 tag: 'tr',
19670                 cn: [
19671                 {
19672                     tag: 'th',
19673                     colspan: '7',
19674                     cls: '',
19675                     cn: [
19676                         {
19677                             tag: 'button',
19678                             cls: 'btn btn-info ok',
19679                             html: 'OK'
19680                         }
19681                     ]
19682                 }
19683
19684                 ]
19685             }
19686         ]
19687     }
19688 });
19689
19690 Roo.apply(Roo.bootstrap.TimeField,  {
19691   
19692     template : {
19693         tag: 'div',
19694         cls: 'datepicker dropdown-menu',
19695         cn: [
19696             {
19697                 tag: 'div',
19698                 cls: 'datepicker-time',
19699                 cn: [
19700                 {
19701                     tag: 'table',
19702                     cls: 'table-condensed',
19703                     cn:[
19704                     Roo.bootstrap.TimeField.content,
19705                     Roo.bootstrap.TimeField.footer
19706                     ]
19707                 }
19708                 ]
19709             }
19710         ]
19711     }
19712 });
19713
19714  
19715
19716  /*
19717  * - LGPL
19718  *
19719  * MonthField
19720  * 
19721  */
19722
19723 /**
19724  * @class Roo.bootstrap.MonthField
19725  * @extends Roo.bootstrap.Input
19726  * Bootstrap MonthField class
19727  * 
19728  * @cfg {String} language default en
19729  * 
19730  * @constructor
19731  * Create a new MonthField
19732  * @param {Object} config The config object
19733  */
19734
19735 Roo.bootstrap.MonthField = function(config){
19736     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19737     
19738     this.addEvents({
19739         /**
19740          * @event show
19741          * Fires when this field show.
19742          * @param {Roo.bootstrap.MonthField} this
19743          * @param {Mixed} date The date value
19744          */
19745         show : true,
19746         /**
19747          * @event show
19748          * Fires when this field hide.
19749          * @param {Roo.bootstrap.MonthField} this
19750          * @param {Mixed} date The date value
19751          */
19752         hide : true,
19753         /**
19754          * @event select
19755          * Fires when select a date.
19756          * @param {Roo.bootstrap.MonthField} this
19757          * @param {String} oldvalue The old value
19758          * @param {String} newvalue The new value
19759          */
19760         select : true
19761     });
19762 };
19763
19764 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19765     
19766     onRender: function(ct, position)
19767     {
19768         
19769         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19770         
19771         this.language = this.language || 'en';
19772         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19773         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19774         
19775         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19776         this.isInline = false;
19777         this.isInput = true;
19778         this.component = this.el.select('.add-on', true).first() || false;
19779         this.component = (this.component && this.component.length === 0) ? false : this.component;
19780         this.hasInput = this.component && this.inputEL().length;
19781         
19782         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19783         
19784         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19785         
19786         this.picker().on('mousedown', this.onMousedown, this);
19787         this.picker().on('click', this.onClick, this);
19788         
19789         this.picker().addClass('datepicker-dropdown');
19790         
19791         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19792             v.setStyle('width', '189px');
19793         });
19794         
19795         this.fillMonths();
19796         
19797         this.update();
19798         
19799         if(this.isInline) {
19800             this.show();
19801         }
19802         
19803     },
19804     
19805     setValue: function(v, suppressEvent)
19806     {   
19807         var o = this.getValue();
19808         
19809         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19810         
19811         this.update();
19812
19813         if(suppressEvent !== true){
19814             this.fireEvent('select', this, o, v);
19815         }
19816         
19817     },
19818     
19819     getValue: function()
19820     {
19821         return this.value;
19822     },
19823     
19824     onClick: function(e) 
19825     {
19826         e.stopPropagation();
19827         e.preventDefault();
19828         
19829         var target = e.getTarget();
19830         
19831         if(target.nodeName.toLowerCase() === 'i'){
19832             target = Roo.get(target).dom.parentNode;
19833         }
19834         
19835         var nodeName = target.nodeName;
19836         var className = target.className;
19837         var html = target.innerHTML;
19838         
19839         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19840             return;
19841         }
19842         
19843         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19844         
19845         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19846         
19847         this.hide();
19848                         
19849     },
19850     
19851     picker : function()
19852     {
19853         return this.pickerEl;
19854     },
19855     
19856     fillMonths: function()
19857     {    
19858         var i = 0;
19859         var months = this.picker().select('>.datepicker-months td', true).first();
19860         
19861         months.dom.innerHTML = '';
19862         
19863         while (i < 12) {
19864             var month = {
19865                 tag: 'span',
19866                 cls: 'month',
19867                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19868             };
19869             
19870             months.createChild(month);
19871         }
19872         
19873     },
19874     
19875     update: function()
19876     {
19877         var _this = this;
19878         
19879         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19880             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19881         }
19882         
19883         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19884             e.removeClass('active');
19885             
19886             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19887                 e.addClass('active');
19888             }
19889         })
19890     },
19891     
19892     place: function()
19893     {
19894         if(this.isInline) {
19895             return;
19896         }
19897         
19898         this.picker().removeClass(['bottom', 'top']);
19899         
19900         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19901             /*
19902              * place to the top of element!
19903              *
19904              */
19905             
19906             this.picker().addClass('top');
19907             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19908             
19909             return;
19910         }
19911         
19912         this.picker().addClass('bottom');
19913         
19914         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19915     },
19916     
19917     onFocus : function()
19918     {
19919         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19920         this.show();
19921     },
19922     
19923     onBlur : function()
19924     {
19925         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19926         
19927         var d = this.inputEl().getValue();
19928         
19929         this.setValue(d);
19930                 
19931         this.hide();
19932     },
19933     
19934     show : function()
19935     {
19936         this.picker().show();
19937         this.picker().select('>.datepicker-months', true).first().show();
19938         this.update();
19939         this.place();
19940         
19941         this.fireEvent('show', this, this.date);
19942     },
19943     
19944     hide : function()
19945     {
19946         if(this.isInline) {
19947             return;
19948         }
19949         this.picker().hide();
19950         this.fireEvent('hide', this, this.date);
19951         
19952     },
19953     
19954     onMousedown: function(e)
19955     {
19956         e.stopPropagation();
19957         e.preventDefault();
19958     },
19959     
19960     keyup: function(e)
19961     {
19962         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19963         this.update();
19964     },
19965
19966     fireKey: function(e)
19967     {
19968         if (!this.picker().isVisible()){
19969             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19970                 this.show();
19971             }
19972             return;
19973         }
19974         
19975         var dir;
19976         
19977         switch(e.keyCode){
19978             case 27: // escape
19979                 this.hide();
19980                 e.preventDefault();
19981                 break;
19982             case 37: // left
19983             case 39: // right
19984                 dir = e.keyCode == 37 ? -1 : 1;
19985                 
19986                 this.vIndex = this.vIndex + dir;
19987                 
19988                 if(this.vIndex < 0){
19989                     this.vIndex = 0;
19990                 }
19991                 
19992                 if(this.vIndex > 11){
19993                     this.vIndex = 11;
19994                 }
19995                 
19996                 if(isNaN(this.vIndex)){
19997                     this.vIndex = 0;
19998                 }
19999                 
20000                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20001                 
20002                 break;
20003             case 38: // up
20004             case 40: // down
20005                 
20006                 dir = e.keyCode == 38 ? -1 : 1;
20007                 
20008                 this.vIndex = this.vIndex + dir * 4;
20009                 
20010                 if(this.vIndex < 0){
20011                     this.vIndex = 0;
20012                 }
20013                 
20014                 if(this.vIndex > 11){
20015                     this.vIndex = 11;
20016                 }
20017                 
20018                 if(isNaN(this.vIndex)){
20019                     this.vIndex = 0;
20020                 }
20021                 
20022                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20023                 break;
20024                 
20025             case 13: // enter
20026                 
20027                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20028                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20029                 }
20030                 
20031                 this.hide();
20032                 e.preventDefault();
20033                 break;
20034             case 9: // tab
20035                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20036                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20037                 }
20038                 this.hide();
20039                 break;
20040             case 16: // shift
20041             case 17: // ctrl
20042             case 18: // alt
20043                 break;
20044             default :
20045                 this.hide();
20046                 
20047         }
20048     },
20049     
20050     remove: function() 
20051     {
20052         this.picker().remove();
20053     }
20054    
20055 });
20056
20057 Roo.apply(Roo.bootstrap.MonthField,  {
20058     
20059     content : {
20060         tag: 'tbody',
20061         cn: [
20062         {
20063             tag: 'tr',
20064             cn: [
20065             {
20066                 tag: 'td',
20067                 colspan: '7'
20068             }
20069             ]
20070         }
20071         ]
20072     },
20073     
20074     dates:{
20075         en: {
20076             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20077             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20078         }
20079     }
20080 });
20081
20082 Roo.apply(Roo.bootstrap.MonthField,  {
20083   
20084     template : {
20085         tag: 'div',
20086         cls: 'datepicker dropdown-menu roo-dynamic',
20087         cn: [
20088             {
20089                 tag: 'div',
20090                 cls: 'datepicker-months',
20091                 cn: [
20092                 {
20093                     tag: 'table',
20094                     cls: 'table-condensed',
20095                     cn:[
20096                         Roo.bootstrap.DateField.content
20097                     ]
20098                 }
20099                 ]
20100             }
20101         ]
20102     }
20103 });
20104
20105  
20106
20107  
20108  /*
20109  * - LGPL
20110  *
20111  * CheckBox
20112  * 
20113  */
20114
20115 /**
20116  * @class Roo.bootstrap.CheckBox
20117  * @extends Roo.bootstrap.Input
20118  * Bootstrap CheckBox class
20119  * 
20120  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20121  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20122  * @cfg {String} boxLabel The text that appears beside the checkbox
20123  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20124  * @cfg {Boolean} checked initnal the element
20125  * @cfg {Boolean} inline inline the element (default false)
20126  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20127  * 
20128  * @constructor
20129  * Create a new CheckBox
20130  * @param {Object} config The config object
20131  */
20132
20133 Roo.bootstrap.CheckBox = function(config){
20134     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20135    
20136     this.addEvents({
20137         /**
20138         * @event check
20139         * Fires when the element is checked or unchecked.
20140         * @param {Roo.bootstrap.CheckBox} this This input
20141         * @param {Boolean} checked The new checked value
20142         */
20143        check : true
20144     });
20145     
20146 };
20147
20148 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20149   
20150     inputType: 'checkbox',
20151     inputValue: 1,
20152     valueOff: 0,
20153     boxLabel: false,
20154     checked: false,
20155     weight : false,
20156     inline: false,
20157     
20158     getAutoCreate : function()
20159     {
20160         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20161         
20162         var id = Roo.id();
20163         
20164         var cfg = {};
20165         
20166         cfg.cls = 'form-group ' + this.inputType; //input-group
20167         
20168         if(this.inline){
20169             cfg.cls += ' ' + this.inputType + '-inline';
20170         }
20171         
20172         var input =  {
20173             tag: 'input',
20174             id : id,
20175             type : this.inputType,
20176             value : this.inputValue,
20177             cls : 'roo-' + this.inputType, //'form-box',
20178             placeholder : this.placeholder || ''
20179             
20180         };
20181         
20182         if(this.inputType != 'radio'){
20183             var hidden =  {
20184                 tag: 'input',
20185                 type : 'hidden',
20186                 cls : 'roo-hidden-value',
20187                 value : this.checked ? this.valueOff : this.inputValue
20188             };
20189         }
20190         
20191             
20192         if (this.weight) { // Validity check?
20193             cfg.cls += " " + this.inputType + "-" + this.weight;
20194         }
20195         
20196         if (this.disabled) {
20197             input.disabled=true;
20198         }
20199         
20200         if(this.checked){
20201             input.checked = this.checked;
20202             
20203         }
20204         
20205         
20206         if (this.name) {
20207             
20208             input.name = this.name;
20209             
20210             if(this.inputType != 'radio'){
20211                 hidden.name = this.name;
20212                 input.name = '_hidden_' + this.name;
20213             }
20214         }
20215         
20216         if (this.size) {
20217             input.cls += ' input-' + this.size;
20218         }
20219         
20220         var settings=this;
20221         
20222         ['xs','sm','md','lg'].map(function(size){
20223             if (settings[size]) {
20224                 cfg.cls += ' col-' + size + '-' + settings[size];
20225             }
20226         });
20227         
20228         var inputblock = input;
20229          
20230         if (this.before || this.after) {
20231             
20232             inputblock = {
20233                 cls : 'input-group',
20234                 cn :  [] 
20235             };
20236             
20237             if (this.before) {
20238                 inputblock.cn.push({
20239                     tag :'span',
20240                     cls : 'input-group-addon',
20241                     html : this.before
20242                 });
20243             }
20244             
20245             inputblock.cn.push(input);
20246             
20247             if(this.inputType != 'radio'){
20248                 inputblock.cn.push(hidden);
20249             }
20250             
20251             if (this.after) {
20252                 inputblock.cn.push({
20253                     tag :'span',
20254                     cls : 'input-group-addon',
20255                     html : this.after
20256                 });
20257             }
20258             
20259         }
20260         
20261         if (align ==='left' && this.fieldLabel.length) {
20262 //                Roo.log("left and has label");
20263             cfg.cn = [
20264                 {
20265                     tag: 'label',
20266                     'for' :  id,
20267                     cls : 'control-label',
20268                     html : this.fieldLabel
20269
20270                 },
20271                 {
20272                     cls : "", 
20273                     cn: [
20274                         inputblock
20275                     ]
20276                 }
20277             ];
20278             
20279             if(this.labelWidth > 12){
20280                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20281             }
20282             
20283             if(this.labelWidth < 13 && this.labelmd == 0){
20284                 this.labelmd = this.labelWidth;
20285             }
20286             
20287             if(this.labellg > 0){
20288                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20289                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20290             }
20291             
20292             if(this.labelmd > 0){
20293                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20294                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20295             }
20296             
20297             if(this.labelsm > 0){
20298                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20299                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20300             }
20301             
20302             if(this.labelxs > 0){
20303                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20304                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20305             }
20306             
20307         } else if ( this.fieldLabel.length) {
20308 //                Roo.log(" label");
20309                 cfg.cn = [
20310                    
20311                     {
20312                         tag: this.boxLabel ? 'span' : 'label',
20313                         'for': id,
20314                         cls: 'control-label box-input-label',
20315                         //cls : 'input-group-addon',
20316                         html : this.fieldLabel
20317                         
20318                     },
20319                     
20320                     inputblock
20321                     
20322                 ];
20323
20324         } else {
20325             
20326 //                Roo.log(" no label && no align");
20327                 cfg.cn = [  inputblock ] ;
20328                 
20329                 
20330         }
20331         
20332         if(this.boxLabel){
20333              var boxLabelCfg = {
20334                 tag: 'label',
20335                 //'for': id, // box label is handled by onclick - so no for...
20336                 cls: 'box-label',
20337                 html: this.boxLabel
20338             };
20339             
20340             if(this.tooltip){
20341                 boxLabelCfg.tooltip = this.tooltip;
20342             }
20343              
20344             cfg.cn.push(boxLabelCfg);
20345         }
20346         
20347         if(this.inputType != 'radio'){
20348             cfg.cn.push(hidden);
20349         }
20350         
20351         return cfg;
20352         
20353     },
20354     
20355     /**
20356      * return the real input element.
20357      */
20358     inputEl: function ()
20359     {
20360         return this.el.select('input.roo-' + this.inputType,true).first();
20361     },
20362     hiddenEl: function ()
20363     {
20364         return this.el.select('input.roo-hidden-value',true).first();
20365     },
20366     
20367     labelEl: function()
20368     {
20369         return this.el.select('label.control-label',true).first();
20370     },
20371     /* depricated... */
20372     
20373     label: function()
20374     {
20375         return this.labelEl();
20376     },
20377     
20378     boxLabelEl: function()
20379     {
20380         return this.el.select('label.box-label',true).first();
20381     },
20382     
20383     initEvents : function()
20384     {
20385 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20386         
20387         this.inputEl().on('click', this.onClick,  this);
20388         
20389         if (this.boxLabel) { 
20390             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20391         }
20392         
20393         this.startValue = this.getValue();
20394         
20395         if(this.groupId){
20396             Roo.bootstrap.CheckBox.register(this);
20397         }
20398     },
20399     
20400     onClick : function()
20401     {   
20402         this.setChecked(!this.checked);
20403     },
20404     
20405     setChecked : function(state,suppressEvent)
20406     {
20407         this.startValue = this.getValue();
20408
20409         if(this.inputType == 'radio'){
20410             
20411             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20412                 e.dom.checked = false;
20413             });
20414             
20415             this.inputEl().dom.checked = true;
20416             
20417             this.inputEl().dom.value = this.inputValue;
20418             
20419             if(suppressEvent !== true){
20420                 this.fireEvent('check', this, true);
20421             }
20422             
20423             this.validate();
20424             
20425             return;
20426         }
20427         
20428         this.checked = state;
20429         
20430         this.inputEl().dom.checked = state;
20431         
20432         
20433         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20434         
20435         if(suppressEvent !== true){
20436             this.fireEvent('check', this, state);
20437         }
20438         
20439         this.validate();
20440     },
20441     
20442     getValue : function()
20443     {
20444         if(this.inputType == 'radio'){
20445             return this.getGroupValue();
20446         }
20447         
20448         return this.hiddenEl().dom.value;
20449         
20450     },
20451     
20452     getGroupValue : function()
20453     {
20454         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20455             return '';
20456         }
20457         
20458         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20459     },
20460     
20461     setValue : function(v,suppressEvent)
20462     {
20463         if(this.inputType == 'radio'){
20464             this.setGroupValue(v, suppressEvent);
20465             return;
20466         }
20467         
20468         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20469         
20470         this.validate();
20471     },
20472     
20473     setGroupValue : function(v, suppressEvent)
20474     {
20475         this.startValue = this.getValue();
20476         
20477         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20478             e.dom.checked = false;
20479             
20480             if(e.dom.value == v){
20481                 e.dom.checked = true;
20482             }
20483         });
20484         
20485         if(suppressEvent !== true){
20486             this.fireEvent('check', this, true);
20487         }
20488
20489         this.validate();
20490         
20491         return;
20492     },
20493     
20494     validate : function()
20495     {
20496         if(
20497                 this.disabled || 
20498                 (this.inputType == 'radio' && this.validateRadio()) ||
20499                 (this.inputType == 'checkbox' && this.validateCheckbox())
20500         ){
20501             this.markValid();
20502             return true;
20503         }
20504         
20505         this.markInvalid();
20506         return false;
20507     },
20508     
20509     validateRadio : function()
20510     {
20511         if(this.allowBlank){
20512             return true;
20513         }
20514         
20515         var valid = false;
20516         
20517         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20518             if(!e.dom.checked){
20519                 return;
20520             }
20521             
20522             valid = true;
20523             
20524             return false;
20525         });
20526         
20527         return valid;
20528     },
20529     
20530     validateCheckbox : function()
20531     {
20532         if(!this.groupId){
20533             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20534             //return (this.getValue() == this.inputValue) ? true : false;
20535         }
20536         
20537         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20538         
20539         if(!group){
20540             return false;
20541         }
20542         
20543         var r = false;
20544         
20545         for(var i in group){
20546             if(r){
20547                 break;
20548             }
20549             
20550             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20551         }
20552         
20553         return r;
20554     },
20555     
20556     /**
20557      * Mark this field as valid
20558      */
20559     markValid : function()
20560     {
20561         var _this = this;
20562         
20563         this.fireEvent('valid', this);
20564         
20565         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20566         
20567         if(this.groupId){
20568             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20569         }
20570         
20571         if(label){
20572             label.markValid();
20573         }
20574
20575         if(this.inputType == 'radio'){
20576             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20577                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20578                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20579             });
20580             
20581             return;
20582         }
20583
20584         if(!this.groupId){
20585             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20586             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20587             return;
20588         }
20589         
20590         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20591         
20592         if(!group){
20593             return;
20594         }
20595         
20596         for(var i in group){
20597             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20598             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20599         }
20600     },
20601     
20602      /**
20603      * Mark this field as invalid
20604      * @param {String} msg The validation message
20605      */
20606     markInvalid : function(msg)
20607     {
20608         if(this.allowBlank){
20609             return;
20610         }
20611         
20612         var _this = this;
20613         
20614         this.fireEvent('invalid', this, msg);
20615         
20616         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20617         
20618         if(this.groupId){
20619             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20620         }
20621         
20622         if(label){
20623             label.markInvalid();
20624         }
20625             
20626         if(this.inputType == 'radio'){
20627             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20628                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20629                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20630             });
20631             
20632             return;
20633         }
20634         
20635         if(!this.groupId){
20636             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20637             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20638             return;
20639         }
20640         
20641         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20642         
20643         if(!group){
20644             return;
20645         }
20646         
20647         for(var i in group){
20648             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20649             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20650         }
20651         
20652     },
20653     
20654     clearInvalid : function()
20655     {
20656         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20657         
20658         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20659         
20660         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20661         
20662         if (label) {
20663             label.iconEl.removeClass(label.validClass);
20664             label.iconEl.removeClass(label.invalidClass);
20665         }
20666     },
20667     
20668     disable : function()
20669     {
20670         if(this.inputType != 'radio'){
20671             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20672             return;
20673         }
20674         
20675         var _this = this;
20676         
20677         if(this.rendered){
20678             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20679                 _this.getActionEl().addClass(this.disabledClass);
20680                 e.dom.disabled = true;
20681             });
20682         }
20683         
20684         this.disabled = true;
20685         this.fireEvent("disable", this);
20686         return this;
20687     },
20688
20689     enable : function()
20690     {
20691         if(this.inputType != 'radio'){
20692             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20693             return;
20694         }
20695         
20696         var _this = this;
20697         
20698         if(this.rendered){
20699             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20700                 _this.getActionEl().removeClass(this.disabledClass);
20701                 e.dom.disabled = false;
20702             });
20703         }
20704         
20705         this.disabled = false;
20706         this.fireEvent("enable", this);
20707         return this;
20708     }
20709
20710 });
20711
20712 Roo.apply(Roo.bootstrap.CheckBox, {
20713     
20714     groups: {},
20715     
20716      /**
20717     * register a CheckBox Group
20718     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20719     */
20720     register : function(checkbox)
20721     {
20722         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20723             this.groups[checkbox.groupId] = {};
20724         }
20725         
20726         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20727             return;
20728         }
20729         
20730         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20731         
20732     },
20733     /**
20734     * fetch a CheckBox Group based on the group ID
20735     * @param {string} the group ID
20736     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20737     */
20738     get: function(groupId) {
20739         if (typeof(this.groups[groupId]) == 'undefined') {
20740             return false;
20741         }
20742         
20743         return this.groups[groupId] ;
20744     }
20745     
20746     
20747 });
20748 /*
20749  * - LGPL
20750  *
20751  * RadioItem
20752  * 
20753  */
20754
20755 /**
20756  * @class Roo.bootstrap.Radio
20757  * @extends Roo.bootstrap.Component
20758  * Bootstrap Radio class
20759  * @cfg {String} boxLabel - the label associated
20760  * @cfg {String} value - the value of radio
20761  * 
20762  * @constructor
20763  * Create a new Radio
20764  * @param {Object} config The config object
20765  */
20766 Roo.bootstrap.Radio = function(config){
20767     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20768     
20769 };
20770
20771 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20772     
20773     boxLabel : '',
20774     
20775     value : '',
20776     
20777     getAutoCreate : function()
20778     {
20779         var cfg = {
20780             tag : 'div',
20781             cls : 'form-group radio',
20782             cn : [
20783                 {
20784                     tag : 'label',
20785                     cls : 'box-label',
20786                     html : this.boxLabel
20787                 }
20788             ]
20789         };
20790         
20791         return cfg;
20792     },
20793     
20794     initEvents : function() 
20795     {
20796         this.parent().register(this);
20797         
20798         this.el.on('click', this.onClick, this);
20799         
20800     },
20801     
20802     onClick : function()
20803     {
20804         this.setChecked(true);
20805     },
20806     
20807     setChecked : function(state, suppressEvent)
20808     {
20809         this.parent().setValue(this.value, suppressEvent);
20810         
20811     },
20812     
20813     setBoxLabel : function(v)
20814     {
20815         this.boxLabel = v;
20816         
20817         if(this.rendered){
20818             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20819         }
20820     }
20821     
20822 });
20823  
20824
20825  /*
20826  * - LGPL
20827  *
20828  * Input
20829  * 
20830  */
20831
20832 /**
20833  * @class Roo.bootstrap.SecurePass
20834  * @extends Roo.bootstrap.Input
20835  * Bootstrap SecurePass class
20836  *
20837  * 
20838  * @constructor
20839  * Create a new SecurePass
20840  * @param {Object} config The config object
20841  */
20842  
20843 Roo.bootstrap.SecurePass = function (config) {
20844     // these go here, so the translation tool can replace them..
20845     this.errors = {
20846         PwdEmpty: "Please type a password, and then retype it to confirm.",
20847         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20848         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20849         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20850         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20851         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20852         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20853         TooWeak: "Your password is Too Weak."
20854     },
20855     this.meterLabel = "Password strength:";
20856     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20857     this.meterClass = [
20858         "roo-password-meter-tooweak", 
20859         "roo-password-meter-weak", 
20860         "roo-password-meter-medium", 
20861         "roo-password-meter-strong", 
20862         "roo-password-meter-grey"
20863     ];
20864     
20865     this.errors = {};
20866     
20867     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20868 }
20869
20870 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20871     /**
20872      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20873      * {
20874      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20875      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20876      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20877      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20878      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20879      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20880      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20881      * })
20882      */
20883     // private
20884     
20885     meterWidth: 300,
20886     errorMsg :'',    
20887     errors: false,
20888     imageRoot: '/',
20889     /**
20890      * @cfg {String/Object} Label for the strength meter (defaults to
20891      * 'Password strength:')
20892      */
20893     // private
20894     meterLabel: '',
20895     /**
20896      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20897      * ['Weak', 'Medium', 'Strong'])
20898      */
20899     // private    
20900     pwdStrengths: false,    
20901     // private
20902     strength: 0,
20903     // private
20904     _lastPwd: null,
20905     // private
20906     kCapitalLetter: 0,
20907     kSmallLetter: 1,
20908     kDigit: 2,
20909     kPunctuation: 3,
20910     
20911     insecure: false,
20912     // private
20913     initEvents: function ()
20914     {
20915         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20916
20917         if (this.el.is('input[type=password]') && Roo.isSafari) {
20918             this.el.on('keydown', this.SafariOnKeyDown, this);
20919         }
20920
20921         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20922     },
20923     // private
20924     onRender: function (ct, position)
20925     {
20926         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20927         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20928         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20929
20930         this.trigger.createChild({
20931                    cn: [
20932                     {
20933                     //id: 'PwdMeter',
20934                     tag: 'div',
20935                     cls: 'roo-password-meter-grey col-xs-12',
20936                     style: {
20937                         //width: 0,
20938                         //width: this.meterWidth + 'px'                                                
20939                         }
20940                     },
20941                     {                            
20942                          cls: 'roo-password-meter-text'                          
20943                     }
20944                 ]            
20945         });
20946
20947          
20948         if (this.hideTrigger) {
20949             this.trigger.setDisplayed(false);
20950         }
20951         this.setSize(this.width || '', this.height || '');
20952     },
20953     // private
20954     onDestroy: function ()
20955     {
20956         if (this.trigger) {
20957             this.trigger.removeAllListeners();
20958             this.trigger.remove();
20959         }
20960         if (this.wrap) {
20961             this.wrap.remove();
20962         }
20963         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20964     },
20965     // private
20966     checkStrength: function ()
20967     {
20968         var pwd = this.inputEl().getValue();
20969         if (pwd == this._lastPwd) {
20970             return;
20971         }
20972
20973         var strength;
20974         if (this.ClientSideStrongPassword(pwd)) {
20975             strength = 3;
20976         } else if (this.ClientSideMediumPassword(pwd)) {
20977             strength = 2;
20978         } else if (this.ClientSideWeakPassword(pwd)) {
20979             strength = 1;
20980         } else {
20981             strength = 0;
20982         }
20983         
20984         Roo.log('strength1: ' + strength);
20985         
20986         //var pm = this.trigger.child('div/div/div').dom;
20987         var pm = this.trigger.child('div/div');
20988         pm.removeClass(this.meterClass);
20989         pm.addClass(this.meterClass[strength]);
20990                 
20991         
20992         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20993                 
20994         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20995         
20996         this._lastPwd = pwd;
20997     },
20998     reset: function ()
20999     {
21000         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21001         
21002         this._lastPwd = '';
21003         
21004         var pm = this.trigger.child('div/div');
21005         pm.removeClass(this.meterClass);
21006         pm.addClass('roo-password-meter-grey');        
21007         
21008         
21009         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21010         
21011         pt.innerHTML = '';
21012         this.inputEl().dom.type='password';
21013     },
21014     // private
21015     validateValue: function (value)
21016     {
21017         
21018         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21019             return false;
21020         }
21021         if (value.length == 0) {
21022             if (this.allowBlank) {
21023                 this.clearInvalid();
21024                 return true;
21025             }
21026
21027             this.markInvalid(this.errors.PwdEmpty);
21028             this.errorMsg = this.errors.PwdEmpty;
21029             return false;
21030         }
21031         
21032         if(this.insecure){
21033             return true;
21034         }
21035         
21036         if ('[\x21-\x7e]*'.match(value)) {
21037             this.markInvalid(this.errors.PwdBadChar);
21038             this.errorMsg = this.errors.PwdBadChar;
21039             return false;
21040         }
21041         if (value.length < 6) {
21042             this.markInvalid(this.errors.PwdShort);
21043             this.errorMsg = this.errors.PwdShort;
21044             return false;
21045         }
21046         if (value.length > 16) {
21047             this.markInvalid(this.errors.PwdLong);
21048             this.errorMsg = this.errors.PwdLong;
21049             return false;
21050         }
21051         var strength;
21052         if (this.ClientSideStrongPassword(value)) {
21053             strength = 3;
21054         } else if (this.ClientSideMediumPassword(value)) {
21055             strength = 2;
21056         } else if (this.ClientSideWeakPassword(value)) {
21057             strength = 1;
21058         } else {
21059             strength = 0;
21060         }
21061
21062         
21063         if (strength < 2) {
21064             //this.markInvalid(this.errors.TooWeak);
21065             this.errorMsg = this.errors.TooWeak;
21066             //return false;
21067         }
21068         
21069         
21070         console.log('strength2: ' + strength);
21071         
21072         //var pm = this.trigger.child('div/div/div').dom;
21073         
21074         var pm = this.trigger.child('div/div');
21075         pm.removeClass(this.meterClass);
21076         pm.addClass(this.meterClass[strength]);
21077                 
21078         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21079                 
21080         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21081         
21082         this.errorMsg = ''; 
21083         return true;
21084     },
21085     // private
21086     CharacterSetChecks: function (type)
21087     {
21088         this.type = type;
21089         this.fResult = false;
21090     },
21091     // private
21092     isctype: function (character, type)
21093     {
21094         switch (type) {  
21095             case this.kCapitalLetter:
21096                 if (character >= 'A' && character <= 'Z') {
21097                     return true;
21098                 }
21099                 break;
21100             
21101             case this.kSmallLetter:
21102                 if (character >= 'a' && character <= 'z') {
21103                     return true;
21104                 }
21105                 break;
21106             
21107             case this.kDigit:
21108                 if (character >= '0' && character <= '9') {
21109                     return true;
21110                 }
21111                 break;
21112             
21113             case this.kPunctuation:
21114                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21115                     return true;
21116                 }
21117                 break;
21118             
21119             default:
21120                 return false;
21121         }
21122
21123     },
21124     // private
21125     IsLongEnough: function (pwd, size)
21126     {
21127         return !(pwd == null || isNaN(size) || pwd.length < size);
21128     },
21129     // private
21130     SpansEnoughCharacterSets: function (word, nb)
21131     {
21132         if (!this.IsLongEnough(word, nb))
21133         {
21134             return false;
21135         }
21136
21137         var characterSetChecks = new Array(
21138             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21139             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21140         );
21141         
21142         for (var index = 0; index < word.length; ++index) {
21143             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21144                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21145                     characterSetChecks[nCharSet].fResult = true;
21146                     break;
21147                 }
21148             }
21149         }
21150
21151         var nCharSets = 0;
21152         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21153             if (characterSetChecks[nCharSet].fResult) {
21154                 ++nCharSets;
21155             }
21156         }
21157
21158         if (nCharSets < nb) {
21159             return false;
21160         }
21161         return true;
21162     },
21163     // private
21164     ClientSideStrongPassword: function (pwd)
21165     {
21166         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21167     },
21168     // private
21169     ClientSideMediumPassword: function (pwd)
21170     {
21171         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21172     },
21173     // private
21174     ClientSideWeakPassword: function (pwd)
21175     {
21176         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21177     }
21178           
21179 })//<script type="text/javascript">
21180
21181 /*
21182  * Based  Ext JS Library 1.1.1
21183  * Copyright(c) 2006-2007, Ext JS, LLC.
21184  * LGPL
21185  *
21186  */
21187  
21188 /**
21189  * @class Roo.HtmlEditorCore
21190  * @extends Roo.Component
21191  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21192  *
21193  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21194  */
21195
21196 Roo.HtmlEditorCore = function(config){
21197     
21198     
21199     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21200     
21201     
21202     this.addEvents({
21203         /**
21204          * @event initialize
21205          * Fires when the editor is fully initialized (including the iframe)
21206          * @param {Roo.HtmlEditorCore} this
21207          */
21208         initialize: true,
21209         /**
21210          * @event activate
21211          * Fires when the editor is first receives the focus. Any insertion must wait
21212          * until after this event.
21213          * @param {Roo.HtmlEditorCore} this
21214          */
21215         activate: true,
21216          /**
21217          * @event beforesync
21218          * Fires before the textarea is updated with content from the editor iframe. Return false
21219          * to cancel the sync.
21220          * @param {Roo.HtmlEditorCore} this
21221          * @param {String} html
21222          */
21223         beforesync: true,
21224          /**
21225          * @event beforepush
21226          * Fires before the iframe editor is updated with content from the textarea. Return false
21227          * to cancel the push.
21228          * @param {Roo.HtmlEditorCore} this
21229          * @param {String} html
21230          */
21231         beforepush: true,
21232          /**
21233          * @event sync
21234          * Fires when the textarea is updated with content from the editor iframe.
21235          * @param {Roo.HtmlEditorCore} this
21236          * @param {String} html
21237          */
21238         sync: true,
21239          /**
21240          * @event push
21241          * Fires when the iframe editor is updated with content from the textarea.
21242          * @param {Roo.HtmlEditorCore} this
21243          * @param {String} html
21244          */
21245         push: true,
21246         
21247         /**
21248          * @event editorevent
21249          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21250          * @param {Roo.HtmlEditorCore} this
21251          */
21252         editorevent: true
21253         
21254     });
21255     
21256     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21257     
21258     // defaults : white / black...
21259     this.applyBlacklists();
21260     
21261     
21262     
21263 };
21264
21265
21266 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21267
21268
21269      /**
21270      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21271      */
21272     
21273     owner : false,
21274     
21275      /**
21276      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21277      *                        Roo.resizable.
21278      */
21279     resizable : false,
21280      /**
21281      * @cfg {Number} height (in pixels)
21282      */   
21283     height: 300,
21284    /**
21285      * @cfg {Number} width (in pixels)
21286      */   
21287     width: 500,
21288     
21289     /**
21290      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21291      * 
21292      */
21293     stylesheets: false,
21294     
21295     // id of frame..
21296     frameId: false,
21297     
21298     // private properties
21299     validationEvent : false,
21300     deferHeight: true,
21301     initialized : false,
21302     activated : false,
21303     sourceEditMode : false,
21304     onFocus : Roo.emptyFn,
21305     iframePad:3,
21306     hideMode:'offsets',
21307     
21308     clearUp: true,
21309     
21310     // blacklist + whitelisted elements..
21311     black: false,
21312     white: false,
21313      
21314     bodyCls : '',
21315
21316     /**
21317      * Protected method that will not generally be called directly. It
21318      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21319      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21320      */
21321     getDocMarkup : function(){
21322         // body styles..
21323         var st = '';
21324         
21325         // inherit styels from page...?? 
21326         if (this.stylesheets === false) {
21327             
21328             Roo.get(document.head).select('style').each(function(node) {
21329                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21330             });
21331             
21332             Roo.get(document.head).select('link').each(function(node) { 
21333                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21334             });
21335             
21336         } else if (!this.stylesheets.length) {
21337                 // simple..
21338                 st = '<style type="text/css">' +
21339                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21340                    '</style>';
21341         } else { 
21342             st = '<style type="text/css">' +
21343                     this.stylesheets +
21344                 '</style>';
21345         }
21346         
21347         st +=  '<style type="text/css">' +
21348             'IMG { cursor: pointer } ' +
21349         '</style>';
21350
21351         var cls = 'roo-htmleditor-body';
21352         
21353         if(this.bodyCls.length){
21354             cls += ' ' + this.bodyCls;
21355         }
21356         
21357         return '<html><head>' + st  +
21358             //<style type="text/css">' +
21359             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21360             //'</style>' +
21361             ' </head><body class="' +  cls + '"></body></html>';
21362     },
21363
21364     // private
21365     onRender : function(ct, position)
21366     {
21367         var _t = this;
21368         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21369         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21370         
21371         
21372         this.el.dom.style.border = '0 none';
21373         this.el.dom.setAttribute('tabIndex', -1);
21374         this.el.addClass('x-hidden hide');
21375         
21376         
21377         
21378         if(Roo.isIE){ // fix IE 1px bogus margin
21379             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21380         }
21381        
21382         
21383         this.frameId = Roo.id();
21384         
21385          
21386         
21387         var iframe = this.owner.wrap.createChild({
21388             tag: 'iframe',
21389             cls: 'form-control', // bootstrap..
21390             id: this.frameId,
21391             name: this.frameId,
21392             frameBorder : 'no',
21393             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21394         }, this.el
21395         );
21396         
21397         
21398         this.iframe = iframe.dom;
21399
21400          this.assignDocWin();
21401         
21402         this.doc.designMode = 'on';
21403        
21404         this.doc.open();
21405         this.doc.write(this.getDocMarkup());
21406         this.doc.close();
21407
21408         
21409         var task = { // must defer to wait for browser to be ready
21410             run : function(){
21411                 //console.log("run task?" + this.doc.readyState);
21412                 this.assignDocWin();
21413                 if(this.doc.body || this.doc.readyState == 'complete'){
21414                     try {
21415                         this.doc.designMode="on";
21416                     } catch (e) {
21417                         return;
21418                     }
21419                     Roo.TaskMgr.stop(task);
21420                     this.initEditor.defer(10, this);
21421                 }
21422             },
21423             interval : 10,
21424             duration: 10000,
21425             scope: this
21426         };
21427         Roo.TaskMgr.start(task);
21428
21429     },
21430
21431     // private
21432     onResize : function(w, h)
21433     {
21434          Roo.log('resize: ' +w + ',' + h );
21435         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21436         if(!this.iframe){
21437             return;
21438         }
21439         if(typeof w == 'number'){
21440             
21441             this.iframe.style.width = w + 'px';
21442         }
21443         if(typeof h == 'number'){
21444             
21445             this.iframe.style.height = h + 'px';
21446             if(this.doc){
21447                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21448             }
21449         }
21450         
21451     },
21452
21453     /**
21454      * Toggles the editor between standard and source edit mode.
21455      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21456      */
21457     toggleSourceEdit : function(sourceEditMode){
21458         
21459         this.sourceEditMode = sourceEditMode === true;
21460         
21461         if(this.sourceEditMode){
21462  
21463             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21464             
21465         }else{
21466             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21467             //this.iframe.className = '';
21468             this.deferFocus();
21469         }
21470         //this.setSize(this.owner.wrap.getSize());
21471         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21472     },
21473
21474     
21475   
21476
21477     /**
21478      * Protected method that will not generally be called directly. If you need/want
21479      * custom HTML cleanup, this is the method you should override.
21480      * @param {String} html The HTML to be cleaned
21481      * return {String} The cleaned HTML
21482      */
21483     cleanHtml : function(html){
21484         html = String(html);
21485         if(html.length > 5){
21486             if(Roo.isSafari){ // strip safari nonsense
21487                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21488             }
21489         }
21490         if(html == '&nbsp;'){
21491             html = '';
21492         }
21493         return html;
21494     },
21495
21496     /**
21497      * HTML Editor -> Textarea
21498      * Protected method that will not generally be called directly. Syncs the contents
21499      * of the editor iframe with the textarea.
21500      */
21501     syncValue : function(){
21502         if(this.initialized){
21503             var bd = (this.doc.body || this.doc.documentElement);
21504             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21505             var html = bd.innerHTML;
21506             if(Roo.isSafari){
21507                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21508                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21509                 if(m && m[1]){
21510                     html = '<div style="'+m[0]+'">' + html + '</div>';
21511                 }
21512             }
21513             html = this.cleanHtml(html);
21514             // fix up the special chars.. normaly like back quotes in word...
21515             // however we do not want to do this with chinese..
21516             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21517                 var cc = b.charCodeAt();
21518                 if (
21519                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21520                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21521                     (cc >= 0xf900 && cc < 0xfb00 )
21522                 ) {
21523                         return b;
21524                 }
21525                 return "&#"+cc+";" 
21526             });
21527             if(this.owner.fireEvent('beforesync', this, html) !== false){
21528                 this.el.dom.value = html;
21529                 this.owner.fireEvent('sync', this, html);
21530             }
21531         }
21532     },
21533
21534     /**
21535      * Protected method that will not generally be called directly. Pushes the value of the textarea
21536      * into the iframe editor.
21537      */
21538     pushValue : function(){
21539         if(this.initialized){
21540             var v = this.el.dom.value.trim();
21541             
21542 //            if(v.length < 1){
21543 //                v = '&#160;';
21544 //            }
21545             
21546             if(this.owner.fireEvent('beforepush', this, v) !== false){
21547                 var d = (this.doc.body || this.doc.documentElement);
21548                 d.innerHTML = v;
21549                 this.cleanUpPaste();
21550                 this.el.dom.value = d.innerHTML;
21551                 this.owner.fireEvent('push', this, v);
21552             }
21553         }
21554     },
21555
21556     // private
21557     deferFocus : function(){
21558         this.focus.defer(10, this);
21559     },
21560
21561     // doc'ed in Field
21562     focus : function(){
21563         if(this.win && !this.sourceEditMode){
21564             this.win.focus();
21565         }else{
21566             this.el.focus();
21567         }
21568     },
21569     
21570     assignDocWin: function()
21571     {
21572         var iframe = this.iframe;
21573         
21574          if(Roo.isIE){
21575             this.doc = iframe.contentWindow.document;
21576             this.win = iframe.contentWindow;
21577         } else {
21578 //            if (!Roo.get(this.frameId)) {
21579 //                return;
21580 //            }
21581 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21582 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21583             
21584             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21585                 return;
21586             }
21587             
21588             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21589             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21590         }
21591     },
21592     
21593     // private
21594     initEditor : function(){
21595         //console.log("INIT EDITOR");
21596         this.assignDocWin();
21597         
21598         
21599         
21600         this.doc.designMode="on";
21601         this.doc.open();
21602         this.doc.write(this.getDocMarkup());
21603         this.doc.close();
21604         
21605         var dbody = (this.doc.body || this.doc.documentElement);
21606         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21607         // this copies styles from the containing element into thsi one..
21608         // not sure why we need all of this..
21609         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21610         
21611         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21612         //ss['background-attachment'] = 'fixed'; // w3c
21613         dbody.bgProperties = 'fixed'; // ie
21614         //Roo.DomHelper.applyStyles(dbody, ss);
21615         Roo.EventManager.on(this.doc, {
21616             //'mousedown': this.onEditorEvent,
21617             'mouseup': this.onEditorEvent,
21618             'dblclick': this.onEditorEvent,
21619             'click': this.onEditorEvent,
21620             'keyup': this.onEditorEvent,
21621             buffer:100,
21622             scope: this
21623         });
21624         if(Roo.isGecko){
21625             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21626         }
21627         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21628             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21629         }
21630         this.initialized = true;
21631
21632         this.owner.fireEvent('initialize', this);
21633         this.pushValue();
21634     },
21635
21636     // private
21637     onDestroy : function(){
21638         
21639         
21640         
21641         if(this.rendered){
21642             
21643             //for (var i =0; i < this.toolbars.length;i++) {
21644             //    // fixme - ask toolbars for heights?
21645             //    this.toolbars[i].onDestroy();
21646            // }
21647             
21648             //this.wrap.dom.innerHTML = '';
21649             //this.wrap.remove();
21650         }
21651     },
21652
21653     // private
21654     onFirstFocus : function(){
21655         
21656         this.assignDocWin();
21657         
21658         
21659         this.activated = true;
21660          
21661     
21662         if(Roo.isGecko){ // prevent silly gecko errors
21663             this.win.focus();
21664             var s = this.win.getSelection();
21665             if(!s.focusNode || s.focusNode.nodeType != 3){
21666                 var r = s.getRangeAt(0);
21667                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21668                 r.collapse(true);
21669                 this.deferFocus();
21670             }
21671             try{
21672                 this.execCmd('useCSS', true);
21673                 this.execCmd('styleWithCSS', false);
21674             }catch(e){}
21675         }
21676         this.owner.fireEvent('activate', this);
21677     },
21678
21679     // private
21680     adjustFont: function(btn){
21681         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21682         //if(Roo.isSafari){ // safari
21683         //    adjust *= 2;
21684        // }
21685         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21686         if(Roo.isSafari){ // safari
21687             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21688             v =  (v < 10) ? 10 : v;
21689             v =  (v > 48) ? 48 : v;
21690             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21691             
21692         }
21693         
21694         
21695         v = Math.max(1, v+adjust);
21696         
21697         this.execCmd('FontSize', v  );
21698     },
21699
21700     onEditorEvent : function(e)
21701     {
21702         this.owner.fireEvent('editorevent', this, e);
21703       //  this.updateToolbar();
21704         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21705     },
21706
21707     insertTag : function(tg)
21708     {
21709         // could be a bit smarter... -> wrap the current selected tRoo..
21710         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21711             
21712             range = this.createRange(this.getSelection());
21713             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21714             wrappingNode.appendChild(range.extractContents());
21715             range.insertNode(wrappingNode);
21716
21717             return;
21718             
21719             
21720             
21721         }
21722         this.execCmd("formatblock",   tg);
21723         
21724     },
21725     
21726     insertText : function(txt)
21727     {
21728         
21729         
21730         var range = this.createRange();
21731         range.deleteContents();
21732                //alert(Sender.getAttribute('label'));
21733                
21734         range.insertNode(this.doc.createTextNode(txt));
21735     } ,
21736     
21737      
21738
21739     /**
21740      * Executes a Midas editor command on the editor document and performs necessary focus and
21741      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21742      * @param {String} cmd The Midas command
21743      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21744      */
21745     relayCmd : function(cmd, value){
21746         this.win.focus();
21747         this.execCmd(cmd, value);
21748         this.owner.fireEvent('editorevent', this);
21749         //this.updateToolbar();
21750         this.owner.deferFocus();
21751     },
21752
21753     /**
21754      * Executes a Midas editor command directly on the editor document.
21755      * For visual commands, you should use {@link #relayCmd} instead.
21756      * <b>This should only be called after the editor is initialized.</b>
21757      * @param {String} cmd The Midas command
21758      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21759      */
21760     execCmd : function(cmd, value){
21761         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21762         this.syncValue();
21763     },
21764  
21765  
21766    
21767     /**
21768      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21769      * to insert tRoo.
21770      * @param {String} text | dom node.. 
21771      */
21772     insertAtCursor : function(text)
21773     {
21774         
21775         if(!this.activated){
21776             return;
21777         }
21778         /*
21779         if(Roo.isIE){
21780             this.win.focus();
21781             var r = this.doc.selection.createRange();
21782             if(r){
21783                 r.collapse(true);
21784                 r.pasteHTML(text);
21785                 this.syncValue();
21786                 this.deferFocus();
21787             
21788             }
21789             return;
21790         }
21791         */
21792         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21793             this.win.focus();
21794             
21795             
21796             // from jquery ui (MIT licenced)
21797             var range, node;
21798             var win = this.win;
21799             
21800             if (win.getSelection && win.getSelection().getRangeAt) {
21801                 range = win.getSelection().getRangeAt(0);
21802                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21803                 range.insertNode(node);
21804             } else if (win.document.selection && win.document.selection.createRange) {
21805                 // no firefox support
21806                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21807                 win.document.selection.createRange().pasteHTML(txt);
21808             } else {
21809                 // no firefox support
21810                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21811                 this.execCmd('InsertHTML', txt);
21812             } 
21813             
21814             this.syncValue();
21815             
21816             this.deferFocus();
21817         }
21818     },
21819  // private
21820     mozKeyPress : function(e){
21821         if(e.ctrlKey){
21822             var c = e.getCharCode(), cmd;
21823           
21824             if(c > 0){
21825                 c = String.fromCharCode(c).toLowerCase();
21826                 switch(c){
21827                     case 'b':
21828                         cmd = 'bold';
21829                         break;
21830                     case 'i':
21831                         cmd = 'italic';
21832                         break;
21833                     
21834                     case 'u':
21835                         cmd = 'underline';
21836                         break;
21837                     
21838                     case 'v':
21839                         this.cleanUpPaste.defer(100, this);
21840                         return;
21841                         
21842                 }
21843                 if(cmd){
21844                     this.win.focus();
21845                     this.execCmd(cmd);
21846                     this.deferFocus();
21847                     e.preventDefault();
21848                 }
21849                 
21850             }
21851         }
21852     },
21853
21854     // private
21855     fixKeys : function(){ // load time branching for fastest keydown performance
21856         if(Roo.isIE){
21857             return function(e){
21858                 var k = e.getKey(), r;
21859                 if(k == e.TAB){
21860                     e.stopEvent();
21861                     r = this.doc.selection.createRange();
21862                     if(r){
21863                         r.collapse(true);
21864                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21865                         this.deferFocus();
21866                     }
21867                     return;
21868                 }
21869                 
21870                 if(k == e.ENTER){
21871                     r = this.doc.selection.createRange();
21872                     if(r){
21873                         var target = r.parentElement();
21874                         if(!target || target.tagName.toLowerCase() != 'li'){
21875                             e.stopEvent();
21876                             r.pasteHTML('<br />');
21877                             r.collapse(false);
21878                             r.select();
21879                         }
21880                     }
21881                 }
21882                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21883                     this.cleanUpPaste.defer(100, this);
21884                     return;
21885                 }
21886                 
21887                 
21888             };
21889         }else if(Roo.isOpera){
21890             return function(e){
21891                 var k = e.getKey();
21892                 if(k == e.TAB){
21893                     e.stopEvent();
21894                     this.win.focus();
21895                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21896                     this.deferFocus();
21897                 }
21898                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21899                     this.cleanUpPaste.defer(100, this);
21900                     return;
21901                 }
21902                 
21903             };
21904         }else if(Roo.isSafari){
21905             return function(e){
21906                 var k = e.getKey();
21907                 
21908                 if(k == e.TAB){
21909                     e.stopEvent();
21910                     this.execCmd('InsertText','\t');
21911                     this.deferFocus();
21912                     return;
21913                 }
21914                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21915                     this.cleanUpPaste.defer(100, this);
21916                     return;
21917                 }
21918                 
21919              };
21920         }
21921     }(),
21922     
21923     getAllAncestors: function()
21924     {
21925         var p = this.getSelectedNode();
21926         var a = [];
21927         if (!p) {
21928             a.push(p); // push blank onto stack..
21929             p = this.getParentElement();
21930         }
21931         
21932         
21933         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21934             a.push(p);
21935             p = p.parentNode;
21936         }
21937         a.push(this.doc.body);
21938         return a;
21939     },
21940     lastSel : false,
21941     lastSelNode : false,
21942     
21943     
21944     getSelection : function() 
21945     {
21946         this.assignDocWin();
21947         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21948     },
21949     
21950     getSelectedNode: function() 
21951     {
21952         // this may only work on Gecko!!!
21953         
21954         // should we cache this!!!!
21955         
21956         
21957         
21958          
21959         var range = this.createRange(this.getSelection()).cloneRange();
21960         
21961         if (Roo.isIE) {
21962             var parent = range.parentElement();
21963             while (true) {
21964                 var testRange = range.duplicate();
21965                 testRange.moveToElementText(parent);
21966                 if (testRange.inRange(range)) {
21967                     break;
21968                 }
21969                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21970                     break;
21971                 }
21972                 parent = parent.parentElement;
21973             }
21974             return parent;
21975         }
21976         
21977         // is ancestor a text element.
21978         var ac =  range.commonAncestorContainer;
21979         if (ac.nodeType == 3) {
21980             ac = ac.parentNode;
21981         }
21982         
21983         var ar = ac.childNodes;
21984          
21985         var nodes = [];
21986         var other_nodes = [];
21987         var has_other_nodes = false;
21988         for (var i=0;i<ar.length;i++) {
21989             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21990                 continue;
21991             }
21992             // fullly contained node.
21993             
21994             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21995                 nodes.push(ar[i]);
21996                 continue;
21997             }
21998             
21999             // probably selected..
22000             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22001                 other_nodes.push(ar[i]);
22002                 continue;
22003             }
22004             // outer..
22005             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22006                 continue;
22007             }
22008             
22009             
22010             has_other_nodes = true;
22011         }
22012         if (!nodes.length && other_nodes.length) {
22013             nodes= other_nodes;
22014         }
22015         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22016             return false;
22017         }
22018         
22019         return nodes[0];
22020     },
22021     createRange: function(sel)
22022     {
22023         // this has strange effects when using with 
22024         // top toolbar - not sure if it's a great idea.
22025         //this.editor.contentWindow.focus();
22026         if (typeof sel != "undefined") {
22027             try {
22028                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22029             } catch(e) {
22030                 return this.doc.createRange();
22031             }
22032         } else {
22033             return this.doc.createRange();
22034         }
22035     },
22036     getParentElement: function()
22037     {
22038         
22039         this.assignDocWin();
22040         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22041         
22042         var range = this.createRange(sel);
22043          
22044         try {
22045             var p = range.commonAncestorContainer;
22046             while (p.nodeType == 3) { // text node
22047                 p = p.parentNode;
22048             }
22049             return p;
22050         } catch (e) {
22051             return null;
22052         }
22053     
22054     },
22055     /***
22056      *
22057      * Range intersection.. the hard stuff...
22058      *  '-1' = before
22059      *  '0' = hits..
22060      *  '1' = after.
22061      *         [ -- selected range --- ]
22062      *   [fail]                        [fail]
22063      *
22064      *    basically..
22065      *      if end is before start or  hits it. fail.
22066      *      if start is after end or hits it fail.
22067      *
22068      *   if either hits (but other is outside. - then it's not 
22069      *   
22070      *    
22071      **/
22072     
22073     
22074     // @see http://www.thismuchiknow.co.uk/?p=64.
22075     rangeIntersectsNode : function(range, node)
22076     {
22077         var nodeRange = node.ownerDocument.createRange();
22078         try {
22079             nodeRange.selectNode(node);
22080         } catch (e) {
22081             nodeRange.selectNodeContents(node);
22082         }
22083     
22084         var rangeStartRange = range.cloneRange();
22085         rangeStartRange.collapse(true);
22086     
22087         var rangeEndRange = range.cloneRange();
22088         rangeEndRange.collapse(false);
22089     
22090         var nodeStartRange = nodeRange.cloneRange();
22091         nodeStartRange.collapse(true);
22092     
22093         var nodeEndRange = nodeRange.cloneRange();
22094         nodeEndRange.collapse(false);
22095     
22096         return rangeStartRange.compareBoundaryPoints(
22097                  Range.START_TO_START, nodeEndRange) == -1 &&
22098                rangeEndRange.compareBoundaryPoints(
22099                  Range.START_TO_START, nodeStartRange) == 1;
22100         
22101          
22102     },
22103     rangeCompareNode : function(range, node)
22104     {
22105         var nodeRange = node.ownerDocument.createRange();
22106         try {
22107             nodeRange.selectNode(node);
22108         } catch (e) {
22109             nodeRange.selectNodeContents(node);
22110         }
22111         
22112         
22113         range.collapse(true);
22114     
22115         nodeRange.collapse(true);
22116      
22117         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22118         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22119          
22120         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22121         
22122         var nodeIsBefore   =  ss == 1;
22123         var nodeIsAfter    = ee == -1;
22124         
22125         if (nodeIsBefore && nodeIsAfter) {
22126             return 0; // outer
22127         }
22128         if (!nodeIsBefore && nodeIsAfter) {
22129             return 1; //right trailed.
22130         }
22131         
22132         if (nodeIsBefore && !nodeIsAfter) {
22133             return 2;  // left trailed.
22134         }
22135         // fully contined.
22136         return 3;
22137     },
22138
22139     // private? - in a new class?
22140     cleanUpPaste :  function()
22141     {
22142         // cleans up the whole document..
22143         Roo.log('cleanuppaste');
22144         
22145         this.cleanUpChildren(this.doc.body);
22146         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22147         if (clean != this.doc.body.innerHTML) {
22148             this.doc.body.innerHTML = clean;
22149         }
22150         
22151     },
22152     
22153     cleanWordChars : function(input) {// change the chars to hex code
22154         var he = Roo.HtmlEditorCore;
22155         
22156         var output = input;
22157         Roo.each(he.swapCodes, function(sw) { 
22158             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22159             
22160             output = output.replace(swapper, sw[1]);
22161         });
22162         
22163         return output;
22164     },
22165     
22166     
22167     cleanUpChildren : function (n)
22168     {
22169         if (!n.childNodes.length) {
22170             return;
22171         }
22172         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22173            this.cleanUpChild(n.childNodes[i]);
22174         }
22175     },
22176     
22177     
22178         
22179     
22180     cleanUpChild : function (node)
22181     {
22182         var ed = this;
22183         //console.log(node);
22184         if (node.nodeName == "#text") {
22185             // clean up silly Windows -- stuff?
22186             return; 
22187         }
22188         if (node.nodeName == "#comment") {
22189             node.parentNode.removeChild(node);
22190             // clean up silly Windows -- stuff?
22191             return; 
22192         }
22193         var lcname = node.tagName.toLowerCase();
22194         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22195         // whitelist of tags..
22196         
22197         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22198             // remove node.
22199             node.parentNode.removeChild(node);
22200             return;
22201             
22202         }
22203         
22204         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22205         
22206         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22207         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22208         
22209         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22210         //    remove_keep_children = true;
22211         //}
22212         
22213         if (remove_keep_children) {
22214             this.cleanUpChildren(node);
22215             // inserts everything just before this node...
22216             while (node.childNodes.length) {
22217                 var cn = node.childNodes[0];
22218                 node.removeChild(cn);
22219                 node.parentNode.insertBefore(cn, node);
22220             }
22221             node.parentNode.removeChild(node);
22222             return;
22223         }
22224         
22225         if (!node.attributes || !node.attributes.length) {
22226             this.cleanUpChildren(node);
22227             return;
22228         }
22229         
22230         function cleanAttr(n,v)
22231         {
22232             
22233             if (v.match(/^\./) || v.match(/^\//)) {
22234                 return;
22235             }
22236             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22237                 return;
22238             }
22239             if (v.match(/^#/)) {
22240                 return;
22241             }
22242 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22243             node.removeAttribute(n);
22244             
22245         }
22246         
22247         var cwhite = this.cwhite;
22248         var cblack = this.cblack;
22249             
22250         function cleanStyle(n,v)
22251         {
22252             if (v.match(/expression/)) { //XSS?? should we even bother..
22253                 node.removeAttribute(n);
22254                 return;
22255             }
22256             
22257             var parts = v.split(/;/);
22258             var clean = [];
22259             
22260             Roo.each(parts, function(p) {
22261                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22262                 if (!p.length) {
22263                     return true;
22264                 }
22265                 var l = p.split(':').shift().replace(/\s+/g,'');
22266                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22267                 
22268                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22269 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22270                     //node.removeAttribute(n);
22271                     return true;
22272                 }
22273                 //Roo.log()
22274                 // only allow 'c whitelisted system attributes'
22275                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22276 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22277                     //node.removeAttribute(n);
22278                     return true;
22279                 }
22280                 
22281                 
22282                  
22283                 
22284                 clean.push(p);
22285                 return true;
22286             });
22287             if (clean.length) { 
22288                 node.setAttribute(n, clean.join(';'));
22289             } else {
22290                 node.removeAttribute(n);
22291             }
22292             
22293         }
22294         
22295         
22296         for (var i = node.attributes.length-1; i > -1 ; i--) {
22297             var a = node.attributes[i];
22298             //console.log(a);
22299             
22300             if (a.name.toLowerCase().substr(0,2)=='on')  {
22301                 node.removeAttribute(a.name);
22302                 continue;
22303             }
22304             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22305                 node.removeAttribute(a.name);
22306                 continue;
22307             }
22308             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22309                 cleanAttr(a.name,a.value); // fixme..
22310                 continue;
22311             }
22312             if (a.name == 'style') {
22313                 cleanStyle(a.name,a.value);
22314                 continue;
22315             }
22316             /// clean up MS crap..
22317             // tecnically this should be a list of valid class'es..
22318             
22319             
22320             if (a.name == 'class') {
22321                 if (a.value.match(/^Mso/)) {
22322                     node.className = '';
22323                 }
22324                 
22325                 if (a.value.match(/^body$/)) {
22326                     node.className = '';
22327                 }
22328                 continue;
22329             }
22330             
22331             // style cleanup!?
22332             // class cleanup?
22333             
22334         }
22335         
22336         
22337         this.cleanUpChildren(node);
22338         
22339         
22340     },
22341     
22342     /**
22343      * Clean up MS wordisms...
22344      */
22345     cleanWord : function(node)
22346     {
22347         
22348         
22349         if (!node) {
22350             this.cleanWord(this.doc.body);
22351             return;
22352         }
22353         if (node.nodeName == "#text") {
22354             // clean up silly Windows -- stuff?
22355             return; 
22356         }
22357         if (node.nodeName == "#comment") {
22358             node.parentNode.removeChild(node);
22359             // clean up silly Windows -- stuff?
22360             return; 
22361         }
22362         
22363         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22364             node.parentNode.removeChild(node);
22365             return;
22366         }
22367         
22368         // remove - but keep children..
22369         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22370             while (node.childNodes.length) {
22371                 var cn = node.childNodes[0];
22372                 node.removeChild(cn);
22373                 node.parentNode.insertBefore(cn, node);
22374             }
22375             node.parentNode.removeChild(node);
22376             this.iterateChildren(node, this.cleanWord);
22377             return;
22378         }
22379         // clean styles
22380         if (node.className.length) {
22381             
22382             var cn = node.className.split(/\W+/);
22383             var cna = [];
22384             Roo.each(cn, function(cls) {
22385                 if (cls.match(/Mso[a-zA-Z]+/)) {
22386                     return;
22387                 }
22388                 cna.push(cls);
22389             });
22390             node.className = cna.length ? cna.join(' ') : '';
22391             if (!cna.length) {
22392                 node.removeAttribute("class");
22393             }
22394         }
22395         
22396         if (node.hasAttribute("lang")) {
22397             node.removeAttribute("lang");
22398         }
22399         
22400         if (node.hasAttribute("style")) {
22401             
22402             var styles = node.getAttribute("style").split(";");
22403             var nstyle = [];
22404             Roo.each(styles, function(s) {
22405                 if (!s.match(/:/)) {
22406                     return;
22407                 }
22408                 var kv = s.split(":");
22409                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22410                     return;
22411                 }
22412                 // what ever is left... we allow.
22413                 nstyle.push(s);
22414             });
22415             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22416             if (!nstyle.length) {
22417                 node.removeAttribute('style');
22418             }
22419         }
22420         this.iterateChildren(node, this.cleanWord);
22421         
22422         
22423         
22424     },
22425     /**
22426      * iterateChildren of a Node, calling fn each time, using this as the scole..
22427      * @param {DomNode} node node to iterate children of.
22428      * @param {Function} fn method of this class to call on each item.
22429      */
22430     iterateChildren : function(node, fn)
22431     {
22432         if (!node.childNodes.length) {
22433                 return;
22434         }
22435         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22436            fn.call(this, node.childNodes[i])
22437         }
22438     },
22439     
22440     
22441     /**
22442      * cleanTableWidths.
22443      *
22444      * Quite often pasting from word etc.. results in tables with column and widths.
22445      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22446      *
22447      */
22448     cleanTableWidths : function(node)
22449     {
22450          
22451          
22452         if (!node) {
22453             this.cleanTableWidths(this.doc.body);
22454             return;
22455         }
22456         
22457         // ignore list...
22458         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22459             return; 
22460         }
22461         Roo.log(node.tagName);
22462         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22463             this.iterateChildren(node, this.cleanTableWidths);
22464             return;
22465         }
22466         if (node.hasAttribute('width')) {
22467             node.removeAttribute('width');
22468         }
22469         
22470          
22471         if (node.hasAttribute("style")) {
22472             // pretty basic...
22473             
22474             var styles = node.getAttribute("style").split(";");
22475             var nstyle = [];
22476             Roo.each(styles, function(s) {
22477                 if (!s.match(/:/)) {
22478                     return;
22479                 }
22480                 var kv = s.split(":");
22481                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22482                     return;
22483                 }
22484                 // what ever is left... we allow.
22485                 nstyle.push(s);
22486             });
22487             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22488             if (!nstyle.length) {
22489                 node.removeAttribute('style');
22490             }
22491         }
22492         
22493         this.iterateChildren(node, this.cleanTableWidths);
22494         
22495         
22496     },
22497     
22498     
22499     
22500     
22501     domToHTML : function(currentElement, depth, nopadtext) {
22502         
22503         depth = depth || 0;
22504         nopadtext = nopadtext || false;
22505     
22506         if (!currentElement) {
22507             return this.domToHTML(this.doc.body);
22508         }
22509         
22510         //Roo.log(currentElement);
22511         var j;
22512         var allText = false;
22513         var nodeName = currentElement.nodeName;
22514         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22515         
22516         if  (nodeName == '#text') {
22517             
22518             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22519         }
22520         
22521         
22522         var ret = '';
22523         if (nodeName != 'BODY') {
22524              
22525             var i = 0;
22526             // Prints the node tagName, such as <A>, <IMG>, etc
22527             if (tagName) {
22528                 var attr = [];
22529                 for(i = 0; i < currentElement.attributes.length;i++) {
22530                     // quoting?
22531                     var aname = currentElement.attributes.item(i).name;
22532                     if (!currentElement.attributes.item(i).value.length) {
22533                         continue;
22534                     }
22535                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22536                 }
22537                 
22538                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22539             } 
22540             else {
22541                 
22542                 // eack
22543             }
22544         } else {
22545             tagName = false;
22546         }
22547         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22548             return ret;
22549         }
22550         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22551             nopadtext = true;
22552         }
22553         
22554         
22555         // Traverse the tree
22556         i = 0;
22557         var currentElementChild = currentElement.childNodes.item(i);
22558         var allText = true;
22559         var innerHTML  = '';
22560         lastnode = '';
22561         while (currentElementChild) {
22562             // Formatting code (indent the tree so it looks nice on the screen)
22563             var nopad = nopadtext;
22564             if (lastnode == 'SPAN') {
22565                 nopad  = true;
22566             }
22567             // text
22568             if  (currentElementChild.nodeName == '#text') {
22569                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22570                 toadd = nopadtext ? toadd : toadd.trim();
22571                 if (!nopad && toadd.length > 80) {
22572                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22573                 }
22574                 innerHTML  += toadd;
22575                 
22576                 i++;
22577                 currentElementChild = currentElement.childNodes.item(i);
22578                 lastNode = '';
22579                 continue;
22580             }
22581             allText = false;
22582             
22583             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22584                 
22585             // Recursively traverse the tree structure of the child node
22586             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22587             lastnode = currentElementChild.nodeName;
22588             i++;
22589             currentElementChild=currentElement.childNodes.item(i);
22590         }
22591         
22592         ret += innerHTML;
22593         
22594         if (!allText) {
22595                 // The remaining code is mostly for formatting the tree
22596             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22597         }
22598         
22599         
22600         if (tagName) {
22601             ret+= "</"+tagName+">";
22602         }
22603         return ret;
22604         
22605     },
22606         
22607     applyBlacklists : function()
22608     {
22609         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22610         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22611         
22612         this.white = [];
22613         this.black = [];
22614         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22615             if (b.indexOf(tag) > -1) {
22616                 return;
22617             }
22618             this.white.push(tag);
22619             
22620         }, this);
22621         
22622         Roo.each(w, function(tag) {
22623             if (b.indexOf(tag) > -1) {
22624                 return;
22625             }
22626             if (this.white.indexOf(tag) > -1) {
22627                 return;
22628             }
22629             this.white.push(tag);
22630             
22631         }, this);
22632         
22633         
22634         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22635             if (w.indexOf(tag) > -1) {
22636                 return;
22637             }
22638             this.black.push(tag);
22639             
22640         }, this);
22641         
22642         Roo.each(b, function(tag) {
22643             if (w.indexOf(tag) > -1) {
22644                 return;
22645             }
22646             if (this.black.indexOf(tag) > -1) {
22647                 return;
22648             }
22649             this.black.push(tag);
22650             
22651         }, this);
22652         
22653         
22654         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22655         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22656         
22657         this.cwhite = [];
22658         this.cblack = [];
22659         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22660             if (b.indexOf(tag) > -1) {
22661                 return;
22662             }
22663             this.cwhite.push(tag);
22664             
22665         }, this);
22666         
22667         Roo.each(w, function(tag) {
22668             if (b.indexOf(tag) > -1) {
22669                 return;
22670             }
22671             if (this.cwhite.indexOf(tag) > -1) {
22672                 return;
22673             }
22674             this.cwhite.push(tag);
22675             
22676         }, this);
22677         
22678         
22679         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22680             if (w.indexOf(tag) > -1) {
22681                 return;
22682             }
22683             this.cblack.push(tag);
22684             
22685         }, this);
22686         
22687         Roo.each(b, function(tag) {
22688             if (w.indexOf(tag) > -1) {
22689                 return;
22690             }
22691             if (this.cblack.indexOf(tag) > -1) {
22692                 return;
22693             }
22694             this.cblack.push(tag);
22695             
22696         }, this);
22697     },
22698     
22699     setStylesheets : function(stylesheets)
22700     {
22701         if(typeof(stylesheets) == 'string'){
22702             Roo.get(this.iframe.contentDocument.head).createChild({
22703                 tag : 'link',
22704                 rel : 'stylesheet',
22705                 type : 'text/css',
22706                 href : stylesheets
22707             });
22708             
22709             return;
22710         }
22711         var _this = this;
22712      
22713         Roo.each(stylesheets, function(s) {
22714             if(!s.length){
22715                 return;
22716             }
22717             
22718             Roo.get(_this.iframe.contentDocument.head).createChild({
22719                 tag : 'link',
22720                 rel : 'stylesheet',
22721                 type : 'text/css',
22722                 href : s
22723             });
22724         });
22725
22726         
22727     },
22728     
22729     removeStylesheets : function()
22730     {
22731         var _this = this;
22732         
22733         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22734             s.remove();
22735         });
22736     },
22737     
22738     setStyle : function(style)
22739     {
22740         Roo.get(this.iframe.contentDocument.head).createChild({
22741             tag : 'style',
22742             type : 'text/css',
22743             html : style
22744         });
22745
22746         return;
22747     }
22748     
22749     // hide stuff that is not compatible
22750     /**
22751      * @event blur
22752      * @hide
22753      */
22754     /**
22755      * @event change
22756      * @hide
22757      */
22758     /**
22759      * @event focus
22760      * @hide
22761      */
22762     /**
22763      * @event specialkey
22764      * @hide
22765      */
22766     /**
22767      * @cfg {String} fieldClass @hide
22768      */
22769     /**
22770      * @cfg {String} focusClass @hide
22771      */
22772     /**
22773      * @cfg {String} autoCreate @hide
22774      */
22775     /**
22776      * @cfg {String} inputType @hide
22777      */
22778     /**
22779      * @cfg {String} invalidClass @hide
22780      */
22781     /**
22782      * @cfg {String} invalidText @hide
22783      */
22784     /**
22785      * @cfg {String} msgFx @hide
22786      */
22787     /**
22788      * @cfg {String} validateOnBlur @hide
22789      */
22790 });
22791
22792 Roo.HtmlEditorCore.white = [
22793         'area', 'br', 'img', 'input', 'hr', 'wbr',
22794         
22795        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22796        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22797        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22798        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22799        'table',   'ul',         'xmp', 
22800        
22801        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22802       'thead',   'tr', 
22803      
22804       'dir', 'menu', 'ol', 'ul', 'dl',
22805        
22806       'embed',  'object'
22807 ];
22808
22809
22810 Roo.HtmlEditorCore.black = [
22811     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22812         'applet', // 
22813         'base',   'basefont', 'bgsound', 'blink',  'body', 
22814         'frame',  'frameset', 'head',    'html',   'ilayer', 
22815         'iframe', 'layer',  'link',     'meta',    'object',   
22816         'script', 'style' ,'title',  'xml' // clean later..
22817 ];
22818 Roo.HtmlEditorCore.clean = [
22819     'script', 'style', 'title', 'xml'
22820 ];
22821 Roo.HtmlEditorCore.remove = [
22822     'font'
22823 ];
22824 // attributes..
22825
22826 Roo.HtmlEditorCore.ablack = [
22827     'on'
22828 ];
22829     
22830 Roo.HtmlEditorCore.aclean = [ 
22831     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22832 ];
22833
22834 // protocols..
22835 Roo.HtmlEditorCore.pwhite= [
22836         'http',  'https',  'mailto'
22837 ];
22838
22839 // white listed style attributes.
22840 Roo.HtmlEditorCore.cwhite= [
22841       //  'text-align', /// default is to allow most things..
22842       
22843          
22844 //        'font-size'//??
22845 ];
22846
22847 // black listed style attributes.
22848 Roo.HtmlEditorCore.cblack= [
22849       //  'font-size' -- this can be set by the project 
22850 ];
22851
22852
22853 Roo.HtmlEditorCore.swapCodes   =[ 
22854     [    8211, "--" ], 
22855     [    8212, "--" ], 
22856     [    8216,  "'" ],  
22857     [    8217, "'" ],  
22858     [    8220, '"' ],  
22859     [    8221, '"' ],  
22860     [    8226, "*" ],  
22861     [    8230, "..." ]
22862 ]; 
22863
22864     /*
22865  * - LGPL
22866  *
22867  * HtmlEditor
22868  * 
22869  */
22870
22871 /**
22872  * @class Roo.bootstrap.HtmlEditor
22873  * @extends Roo.bootstrap.TextArea
22874  * Bootstrap HtmlEditor class
22875
22876  * @constructor
22877  * Create a new HtmlEditor
22878  * @param {Object} config The config object
22879  */
22880
22881 Roo.bootstrap.HtmlEditor = function(config){
22882     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22883     if (!this.toolbars) {
22884         this.toolbars = [];
22885     }
22886     
22887     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22888     this.addEvents({
22889             /**
22890              * @event initialize
22891              * Fires when the editor is fully initialized (including the iframe)
22892              * @param {HtmlEditor} this
22893              */
22894             initialize: true,
22895             /**
22896              * @event activate
22897              * Fires when the editor is first receives the focus. Any insertion must wait
22898              * until after this event.
22899              * @param {HtmlEditor} this
22900              */
22901             activate: true,
22902              /**
22903              * @event beforesync
22904              * Fires before the textarea is updated with content from the editor iframe. Return false
22905              * to cancel the sync.
22906              * @param {HtmlEditor} this
22907              * @param {String} html
22908              */
22909             beforesync: true,
22910              /**
22911              * @event beforepush
22912              * Fires before the iframe editor is updated with content from the textarea. Return false
22913              * to cancel the push.
22914              * @param {HtmlEditor} this
22915              * @param {String} html
22916              */
22917             beforepush: true,
22918              /**
22919              * @event sync
22920              * Fires when the textarea is updated with content from the editor iframe.
22921              * @param {HtmlEditor} this
22922              * @param {String} html
22923              */
22924             sync: true,
22925              /**
22926              * @event push
22927              * Fires when the iframe editor is updated with content from the textarea.
22928              * @param {HtmlEditor} this
22929              * @param {String} html
22930              */
22931             push: true,
22932              /**
22933              * @event editmodechange
22934              * Fires when the editor switches edit modes
22935              * @param {HtmlEditor} this
22936              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22937              */
22938             editmodechange: true,
22939             /**
22940              * @event editorevent
22941              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22942              * @param {HtmlEditor} this
22943              */
22944             editorevent: true,
22945             /**
22946              * @event firstfocus
22947              * Fires when on first focus - needed by toolbars..
22948              * @param {HtmlEditor} this
22949              */
22950             firstfocus: true,
22951             /**
22952              * @event autosave
22953              * Auto save the htmlEditor value as a file into Events
22954              * @param {HtmlEditor} this
22955              */
22956             autosave: true,
22957             /**
22958              * @event savedpreview
22959              * preview the saved version of htmlEditor
22960              * @param {HtmlEditor} this
22961              */
22962             savedpreview: true
22963         });
22964 };
22965
22966
22967 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22968     
22969     
22970       /**
22971      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22972      */
22973     toolbars : false,
22974     
22975      /**
22976     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22977     */
22978     btns : [],
22979    
22980      /**
22981      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22982      *                        Roo.resizable.
22983      */
22984     resizable : false,
22985      /**
22986      * @cfg {Number} height (in pixels)
22987      */   
22988     height: 300,
22989    /**
22990      * @cfg {Number} width (in pixels)
22991      */   
22992     width: false,
22993     
22994     /**
22995      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22996      * 
22997      */
22998     stylesheets: false,
22999     
23000     // id of frame..
23001     frameId: false,
23002     
23003     // private properties
23004     validationEvent : false,
23005     deferHeight: true,
23006     initialized : false,
23007     activated : false,
23008     
23009     onFocus : Roo.emptyFn,
23010     iframePad:3,
23011     hideMode:'offsets',
23012     
23013     tbContainer : false,
23014     
23015     bodyCls : '',
23016     
23017     toolbarContainer :function() {
23018         return this.wrap.select('.x-html-editor-tb',true).first();
23019     },
23020
23021     /**
23022      * Protected method that will not generally be called directly. It
23023      * is called when the editor creates its toolbar. Override this method if you need to
23024      * add custom toolbar buttons.
23025      * @param {HtmlEditor} editor
23026      */
23027     createToolbar : function(){
23028         Roo.log('renewing');
23029         Roo.log("create toolbars");
23030         
23031         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23032         this.toolbars[0].render(this.toolbarContainer());
23033         
23034         return;
23035         
23036 //        if (!editor.toolbars || !editor.toolbars.length) {
23037 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23038 //        }
23039 //        
23040 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23041 //            editor.toolbars[i] = Roo.factory(
23042 //                    typeof(editor.toolbars[i]) == 'string' ?
23043 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23044 //                Roo.bootstrap.HtmlEditor);
23045 //            editor.toolbars[i].init(editor);
23046 //        }
23047     },
23048
23049      
23050     // private
23051     onRender : function(ct, position)
23052     {
23053        // Roo.log("Call onRender: " + this.xtype);
23054         var _t = this;
23055         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23056       
23057         this.wrap = this.inputEl().wrap({
23058             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23059         });
23060         
23061         this.editorcore.onRender(ct, position);
23062          
23063         if (this.resizable) {
23064             this.resizeEl = new Roo.Resizable(this.wrap, {
23065                 pinned : true,
23066                 wrap: true,
23067                 dynamic : true,
23068                 minHeight : this.height,
23069                 height: this.height,
23070                 handles : this.resizable,
23071                 width: this.width,
23072                 listeners : {
23073                     resize : function(r, w, h) {
23074                         _t.onResize(w,h); // -something
23075                     }
23076                 }
23077             });
23078             
23079         }
23080         this.createToolbar(this);
23081        
23082         
23083         if(!this.width && this.resizable){
23084             this.setSize(this.wrap.getSize());
23085         }
23086         if (this.resizeEl) {
23087             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23088             // should trigger onReize..
23089         }
23090         
23091     },
23092
23093     // private
23094     onResize : function(w, h)
23095     {
23096         Roo.log('resize: ' +w + ',' + h );
23097         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23098         var ew = false;
23099         var eh = false;
23100         
23101         if(this.inputEl() ){
23102             if(typeof w == 'number'){
23103                 var aw = w - this.wrap.getFrameWidth('lr');
23104                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23105                 ew = aw;
23106             }
23107             if(typeof h == 'number'){
23108                  var tbh = -11;  // fixme it needs to tool bar size!
23109                 for (var i =0; i < this.toolbars.length;i++) {
23110                     // fixme - ask toolbars for heights?
23111                     tbh += this.toolbars[i].el.getHeight();
23112                     //if (this.toolbars[i].footer) {
23113                     //    tbh += this.toolbars[i].footer.el.getHeight();
23114                     //}
23115                 }
23116               
23117                 
23118                 
23119                 
23120                 
23121                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23122                 ah -= 5; // knock a few pixes off for look..
23123                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23124                 var eh = ah;
23125             }
23126         }
23127         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23128         this.editorcore.onResize(ew,eh);
23129         
23130     },
23131
23132     /**
23133      * Toggles the editor between standard and source edit mode.
23134      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23135      */
23136     toggleSourceEdit : function(sourceEditMode)
23137     {
23138         this.editorcore.toggleSourceEdit(sourceEditMode);
23139         
23140         if(this.editorcore.sourceEditMode){
23141             Roo.log('editor - showing textarea');
23142             
23143 //            Roo.log('in');
23144 //            Roo.log(this.syncValue());
23145             this.syncValue();
23146             this.inputEl().removeClass(['hide', 'x-hidden']);
23147             this.inputEl().dom.removeAttribute('tabIndex');
23148             this.inputEl().focus();
23149         }else{
23150             Roo.log('editor - hiding textarea');
23151 //            Roo.log('out')
23152 //            Roo.log(this.pushValue()); 
23153             this.pushValue();
23154             
23155             this.inputEl().addClass(['hide', 'x-hidden']);
23156             this.inputEl().dom.setAttribute('tabIndex', -1);
23157             //this.deferFocus();
23158         }
23159          
23160         if(this.resizable){
23161             this.setSize(this.wrap.getSize());
23162         }
23163         
23164         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23165     },
23166  
23167     // private (for BoxComponent)
23168     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23169
23170     // private (for BoxComponent)
23171     getResizeEl : function(){
23172         return this.wrap;
23173     },
23174
23175     // private (for BoxComponent)
23176     getPositionEl : function(){
23177         return this.wrap;
23178     },
23179
23180     // private
23181     initEvents : function(){
23182         this.originalValue = this.getValue();
23183     },
23184
23185 //    /**
23186 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23187 //     * @method
23188 //     */
23189 //    markInvalid : Roo.emptyFn,
23190 //    /**
23191 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23192 //     * @method
23193 //     */
23194 //    clearInvalid : Roo.emptyFn,
23195
23196     setValue : function(v){
23197         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23198         this.editorcore.pushValue();
23199     },
23200
23201      
23202     // private
23203     deferFocus : function(){
23204         this.focus.defer(10, this);
23205     },
23206
23207     // doc'ed in Field
23208     focus : function(){
23209         this.editorcore.focus();
23210         
23211     },
23212       
23213
23214     // private
23215     onDestroy : function(){
23216         
23217         
23218         
23219         if(this.rendered){
23220             
23221             for (var i =0; i < this.toolbars.length;i++) {
23222                 // fixme - ask toolbars for heights?
23223                 this.toolbars[i].onDestroy();
23224             }
23225             
23226             this.wrap.dom.innerHTML = '';
23227             this.wrap.remove();
23228         }
23229     },
23230
23231     // private
23232     onFirstFocus : function(){
23233         //Roo.log("onFirstFocus");
23234         this.editorcore.onFirstFocus();
23235          for (var i =0; i < this.toolbars.length;i++) {
23236             this.toolbars[i].onFirstFocus();
23237         }
23238         
23239     },
23240     
23241     // private
23242     syncValue : function()
23243     {   
23244         this.editorcore.syncValue();
23245     },
23246     
23247     pushValue : function()
23248     {   
23249         this.editorcore.pushValue();
23250     }
23251      
23252     
23253     // hide stuff that is not compatible
23254     /**
23255      * @event blur
23256      * @hide
23257      */
23258     /**
23259      * @event change
23260      * @hide
23261      */
23262     /**
23263      * @event focus
23264      * @hide
23265      */
23266     /**
23267      * @event specialkey
23268      * @hide
23269      */
23270     /**
23271      * @cfg {String} fieldClass @hide
23272      */
23273     /**
23274      * @cfg {String} focusClass @hide
23275      */
23276     /**
23277      * @cfg {String} autoCreate @hide
23278      */
23279     /**
23280      * @cfg {String} inputType @hide
23281      */
23282     /**
23283      * @cfg {String} invalidClass @hide
23284      */
23285     /**
23286      * @cfg {String} invalidText @hide
23287      */
23288     /**
23289      * @cfg {String} msgFx @hide
23290      */
23291     /**
23292      * @cfg {String} validateOnBlur @hide
23293      */
23294 });
23295  
23296     
23297    
23298    
23299    
23300       
23301 Roo.namespace('Roo.bootstrap.htmleditor');
23302 /**
23303  * @class Roo.bootstrap.HtmlEditorToolbar1
23304  * Basic Toolbar
23305  * 
23306  * Usage:
23307  *
23308  new Roo.bootstrap.HtmlEditor({
23309     ....
23310     toolbars : [
23311         new Roo.bootstrap.HtmlEditorToolbar1({
23312             disable : { fonts: 1 , format: 1, ..., ... , ...],
23313             btns : [ .... ]
23314         })
23315     }
23316      
23317  * 
23318  * @cfg {Object} disable List of elements to disable..
23319  * @cfg {Array} btns List of additional buttons.
23320  * 
23321  * 
23322  * NEEDS Extra CSS? 
23323  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23324  */
23325  
23326 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23327 {
23328     
23329     Roo.apply(this, config);
23330     
23331     // default disabled, based on 'good practice'..
23332     this.disable = this.disable || {};
23333     Roo.applyIf(this.disable, {
23334         fontSize : true,
23335         colors : true,
23336         specialElements : true
23337     });
23338     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23339     
23340     this.editor = config.editor;
23341     this.editorcore = config.editor.editorcore;
23342     
23343     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23344     
23345     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23346     // dont call parent... till later.
23347 }
23348 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23349      
23350     bar : true,
23351     
23352     editor : false,
23353     editorcore : false,
23354     
23355     
23356     formats : [
23357         "p" ,  
23358         "h1","h2","h3","h4","h5","h6", 
23359         "pre", "code", 
23360         "abbr", "acronym", "address", "cite", "samp", "var",
23361         'div','span'
23362     ],
23363     
23364     onRender : function(ct, position)
23365     {
23366        // Roo.log("Call onRender: " + this.xtype);
23367         
23368        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23369        Roo.log(this.el);
23370        this.el.dom.style.marginBottom = '0';
23371        var _this = this;
23372        var editorcore = this.editorcore;
23373        var editor= this.editor;
23374        
23375        var children = [];
23376        var btn = function(id,cmd , toggle, handler, html){
23377        
23378             var  event = toggle ? 'toggle' : 'click';
23379        
23380             var a = {
23381                 size : 'sm',
23382                 xtype: 'Button',
23383                 xns: Roo.bootstrap,
23384                 glyphicon : id,
23385                 cmd : id || cmd,
23386                 enableToggle:toggle !== false,
23387                 html : html || '',
23388                 pressed : toggle ? false : null,
23389                 listeners : {}
23390             };
23391             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23392                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23393             };
23394             children.push(a);
23395             return a;
23396        }
23397        
23398     //    var cb_box = function...
23399         
23400         var style = {
23401                 xtype: 'Button',
23402                 size : 'sm',
23403                 xns: Roo.bootstrap,
23404                 glyphicon : 'font',
23405                 //html : 'submit'
23406                 menu : {
23407                     xtype: 'Menu',
23408                     xns: Roo.bootstrap,
23409                     items:  []
23410                 }
23411         };
23412         Roo.each(this.formats, function(f) {
23413             style.menu.items.push({
23414                 xtype :'MenuItem',
23415                 xns: Roo.bootstrap,
23416                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23417                 tagname : f,
23418                 listeners : {
23419                     click : function()
23420                     {
23421                         editorcore.insertTag(this.tagname);
23422                         editor.focus();
23423                     }
23424                 }
23425                 
23426             });
23427         });
23428         children.push(style);   
23429         
23430         btn('bold',false,true);
23431         btn('italic',false,true);
23432         btn('align-left', 'justifyleft',true);
23433         btn('align-center', 'justifycenter',true);
23434         btn('align-right' , 'justifyright',true);
23435         btn('link', false, false, function(btn) {
23436             //Roo.log("create link?");
23437             var url = prompt(this.createLinkText, this.defaultLinkValue);
23438             if(url && url != 'http:/'+'/'){
23439                 this.editorcore.relayCmd('createlink', url);
23440             }
23441         }),
23442         btn('list','insertunorderedlist',true);
23443         btn('pencil', false,true, function(btn){
23444                 Roo.log(this);
23445                 this.toggleSourceEdit(btn.pressed);
23446         });
23447         
23448         if (this.editor.btns.length > 0) {
23449             for (var i = 0; i<this.editor.btns.length; i++) {
23450                 children.push(this.editor.btns[i]);
23451             }
23452         }
23453         
23454         /*
23455         var cog = {
23456                 xtype: 'Button',
23457                 size : 'sm',
23458                 xns: Roo.bootstrap,
23459                 glyphicon : 'cog',
23460                 //html : 'submit'
23461                 menu : {
23462                     xtype: 'Menu',
23463                     xns: Roo.bootstrap,
23464                     items:  []
23465                 }
23466         };
23467         
23468         cog.menu.items.push({
23469             xtype :'MenuItem',
23470             xns: Roo.bootstrap,
23471             html : Clean styles,
23472             tagname : f,
23473             listeners : {
23474                 click : function()
23475                 {
23476                     editorcore.insertTag(this.tagname);
23477                     editor.focus();
23478                 }
23479             }
23480             
23481         });
23482        */
23483         
23484          
23485        this.xtype = 'NavSimplebar';
23486         
23487         for(var i=0;i< children.length;i++) {
23488             
23489             this.buttons.add(this.addxtypeChild(children[i]));
23490             
23491         }
23492         
23493         editor.on('editorevent', this.updateToolbar, this);
23494     },
23495     onBtnClick : function(id)
23496     {
23497        this.editorcore.relayCmd(id);
23498        this.editorcore.focus();
23499     },
23500     
23501     /**
23502      * Protected method that will not generally be called directly. It triggers
23503      * a toolbar update by reading the markup state of the current selection in the editor.
23504      */
23505     updateToolbar: function(){
23506
23507         if(!this.editorcore.activated){
23508             this.editor.onFirstFocus(); // is this neeed?
23509             return;
23510         }
23511
23512         var btns = this.buttons; 
23513         var doc = this.editorcore.doc;
23514         btns.get('bold').setActive(doc.queryCommandState('bold'));
23515         btns.get('italic').setActive(doc.queryCommandState('italic'));
23516         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23517         
23518         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23519         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23520         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23521         
23522         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23523         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23524          /*
23525         
23526         var ans = this.editorcore.getAllAncestors();
23527         if (this.formatCombo) {
23528             
23529             
23530             var store = this.formatCombo.store;
23531             this.formatCombo.setValue("");
23532             for (var i =0; i < ans.length;i++) {
23533                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23534                     // select it..
23535                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23536                     break;
23537                 }
23538             }
23539         }
23540         
23541         
23542         
23543         // hides menus... - so this cant be on a menu...
23544         Roo.bootstrap.MenuMgr.hideAll();
23545         */
23546         Roo.bootstrap.MenuMgr.hideAll();
23547         //this.editorsyncValue();
23548     },
23549     onFirstFocus: function() {
23550         this.buttons.each(function(item){
23551            item.enable();
23552         });
23553     },
23554     toggleSourceEdit : function(sourceEditMode){
23555         
23556           
23557         if(sourceEditMode){
23558             Roo.log("disabling buttons");
23559            this.buttons.each( function(item){
23560                 if(item.cmd != 'pencil'){
23561                     item.disable();
23562                 }
23563             });
23564           
23565         }else{
23566             Roo.log("enabling buttons");
23567             if(this.editorcore.initialized){
23568                 this.buttons.each( function(item){
23569                     item.enable();
23570                 });
23571             }
23572             
23573         }
23574         Roo.log("calling toggole on editor");
23575         // tell the editor that it's been pressed..
23576         this.editor.toggleSourceEdit(sourceEditMode);
23577        
23578     }
23579 });
23580
23581
23582
23583
23584
23585 /**
23586  * @class Roo.bootstrap.Table.AbstractSelectionModel
23587  * @extends Roo.util.Observable
23588  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23589  * implemented by descendant classes.  This class should not be directly instantiated.
23590  * @constructor
23591  */
23592 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23593     this.locked = false;
23594     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23595 };
23596
23597
23598 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23599     /** @ignore Called by the grid automatically. Do not call directly. */
23600     init : function(grid){
23601         this.grid = grid;
23602         this.initEvents();
23603     },
23604
23605     /**
23606      * Locks the selections.
23607      */
23608     lock : function(){
23609         this.locked = true;
23610     },
23611
23612     /**
23613      * Unlocks the selections.
23614      */
23615     unlock : function(){
23616         this.locked = false;
23617     },
23618
23619     /**
23620      * Returns true if the selections are locked.
23621      * @return {Boolean}
23622      */
23623     isLocked : function(){
23624         return this.locked;
23625     }
23626 });
23627 /**
23628  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23629  * @class Roo.bootstrap.Table.RowSelectionModel
23630  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23631  * It supports multiple selections and keyboard selection/navigation. 
23632  * @constructor
23633  * @param {Object} config
23634  */
23635
23636 Roo.bootstrap.Table.RowSelectionModel = function(config){
23637     Roo.apply(this, config);
23638     this.selections = new Roo.util.MixedCollection(false, function(o){
23639         return o.id;
23640     });
23641
23642     this.last = false;
23643     this.lastActive = false;
23644
23645     this.addEvents({
23646         /**
23647              * @event selectionchange
23648              * Fires when the selection changes
23649              * @param {SelectionModel} this
23650              */
23651             "selectionchange" : true,
23652         /**
23653              * @event afterselectionchange
23654              * Fires after the selection changes (eg. by key press or clicking)
23655              * @param {SelectionModel} this
23656              */
23657             "afterselectionchange" : true,
23658         /**
23659              * @event beforerowselect
23660              * Fires when a row is selected being selected, return false to cancel.
23661              * @param {SelectionModel} this
23662              * @param {Number} rowIndex The selected index
23663              * @param {Boolean} keepExisting False if other selections will be cleared
23664              */
23665             "beforerowselect" : true,
23666         /**
23667              * @event rowselect
23668              * Fires when a row is selected.
23669              * @param {SelectionModel} this
23670              * @param {Number} rowIndex The selected index
23671              * @param {Roo.data.Record} r The record
23672              */
23673             "rowselect" : true,
23674         /**
23675              * @event rowdeselect
23676              * Fires when a row is deselected.
23677              * @param {SelectionModel} this
23678              * @param {Number} rowIndex The selected index
23679              */
23680         "rowdeselect" : true
23681     });
23682     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23683     this.locked = false;
23684  };
23685
23686 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23687     /**
23688      * @cfg {Boolean} singleSelect
23689      * True to allow selection of only one row at a time (defaults to false)
23690      */
23691     singleSelect : false,
23692
23693     // private
23694     initEvents : function()
23695     {
23696
23697         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23698         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23699         //}else{ // allow click to work like normal
23700          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23701         //}
23702         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23703         this.grid.on("rowclick", this.handleMouseDown, this);
23704         
23705         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23706             "up" : function(e){
23707                 if(!e.shiftKey){
23708                     this.selectPrevious(e.shiftKey);
23709                 }else if(this.last !== false && this.lastActive !== false){
23710                     var last = this.last;
23711                     this.selectRange(this.last,  this.lastActive-1);
23712                     this.grid.getView().focusRow(this.lastActive);
23713                     if(last !== false){
23714                         this.last = last;
23715                     }
23716                 }else{
23717                     this.selectFirstRow();
23718                 }
23719                 this.fireEvent("afterselectionchange", this);
23720             },
23721             "down" : function(e){
23722                 if(!e.shiftKey){
23723                     this.selectNext(e.shiftKey);
23724                 }else if(this.last !== false && this.lastActive !== false){
23725                     var last = this.last;
23726                     this.selectRange(this.last,  this.lastActive+1);
23727                     this.grid.getView().focusRow(this.lastActive);
23728                     if(last !== false){
23729                         this.last = last;
23730                     }
23731                 }else{
23732                     this.selectFirstRow();
23733                 }
23734                 this.fireEvent("afterselectionchange", this);
23735             },
23736             scope: this
23737         });
23738         this.grid.store.on('load', function(){
23739             this.selections.clear();
23740         },this);
23741         /*
23742         var view = this.grid.view;
23743         view.on("refresh", this.onRefresh, this);
23744         view.on("rowupdated", this.onRowUpdated, this);
23745         view.on("rowremoved", this.onRemove, this);
23746         */
23747     },
23748
23749     // private
23750     onRefresh : function()
23751     {
23752         var ds = this.grid.store, i, v = this.grid.view;
23753         var s = this.selections;
23754         s.each(function(r){
23755             if((i = ds.indexOfId(r.id)) != -1){
23756                 v.onRowSelect(i);
23757             }else{
23758                 s.remove(r);
23759             }
23760         });
23761     },
23762
23763     // private
23764     onRemove : function(v, index, r){
23765         this.selections.remove(r);
23766     },
23767
23768     // private
23769     onRowUpdated : function(v, index, r){
23770         if(this.isSelected(r)){
23771             v.onRowSelect(index);
23772         }
23773     },
23774
23775     /**
23776      * Select records.
23777      * @param {Array} records The records to select
23778      * @param {Boolean} keepExisting (optional) True to keep existing selections
23779      */
23780     selectRecords : function(records, keepExisting)
23781     {
23782         if(!keepExisting){
23783             this.clearSelections();
23784         }
23785             var ds = this.grid.store;
23786         for(var i = 0, len = records.length; i < len; i++){
23787             this.selectRow(ds.indexOf(records[i]), true);
23788         }
23789     },
23790
23791     /**
23792      * Gets the number of selected rows.
23793      * @return {Number}
23794      */
23795     getCount : function(){
23796         return this.selections.length;
23797     },
23798
23799     /**
23800      * Selects the first row in the grid.
23801      */
23802     selectFirstRow : function(){
23803         this.selectRow(0);
23804     },
23805
23806     /**
23807      * Select the last row.
23808      * @param {Boolean} keepExisting (optional) True to keep existing selections
23809      */
23810     selectLastRow : function(keepExisting){
23811         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23812         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23813     },
23814
23815     /**
23816      * Selects the row immediately following the last selected row.
23817      * @param {Boolean} keepExisting (optional) True to keep existing selections
23818      */
23819     selectNext : function(keepExisting)
23820     {
23821             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23822             this.selectRow(this.last+1, keepExisting);
23823             this.grid.getView().focusRow(this.last);
23824         }
23825     },
23826
23827     /**
23828      * Selects the row that precedes the last selected row.
23829      * @param {Boolean} keepExisting (optional) True to keep existing selections
23830      */
23831     selectPrevious : function(keepExisting){
23832         if(this.last){
23833             this.selectRow(this.last-1, keepExisting);
23834             this.grid.getView().focusRow(this.last);
23835         }
23836     },
23837
23838     /**
23839      * Returns the selected records
23840      * @return {Array} Array of selected records
23841      */
23842     getSelections : function(){
23843         return [].concat(this.selections.items);
23844     },
23845
23846     /**
23847      * Returns the first selected record.
23848      * @return {Record}
23849      */
23850     getSelected : function(){
23851         return this.selections.itemAt(0);
23852     },
23853
23854
23855     /**
23856      * Clears all selections.
23857      */
23858     clearSelections : function(fast)
23859     {
23860         if(this.locked) {
23861             return;
23862         }
23863         if(fast !== true){
23864                 var ds = this.grid.store;
23865             var s = this.selections;
23866             s.each(function(r){
23867                 this.deselectRow(ds.indexOfId(r.id));
23868             }, this);
23869             s.clear();
23870         }else{
23871             this.selections.clear();
23872         }
23873         this.last = false;
23874     },
23875
23876
23877     /**
23878      * Selects all rows.
23879      */
23880     selectAll : function(){
23881         if(this.locked) {
23882             return;
23883         }
23884         this.selections.clear();
23885         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23886             this.selectRow(i, true);
23887         }
23888     },
23889
23890     /**
23891      * Returns True if there is a selection.
23892      * @return {Boolean}
23893      */
23894     hasSelection : function(){
23895         return this.selections.length > 0;
23896     },
23897
23898     /**
23899      * Returns True if the specified row is selected.
23900      * @param {Number/Record} record The record or index of the record to check
23901      * @return {Boolean}
23902      */
23903     isSelected : function(index){
23904             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23905         return (r && this.selections.key(r.id) ? true : false);
23906     },
23907
23908     /**
23909      * Returns True if the specified record id is selected.
23910      * @param {String} id The id of record to check
23911      * @return {Boolean}
23912      */
23913     isIdSelected : function(id){
23914         return (this.selections.key(id) ? true : false);
23915     },
23916
23917
23918     // private
23919     handleMouseDBClick : function(e, t){
23920         
23921     },
23922     // private
23923     handleMouseDown : function(e, t)
23924     {
23925             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23926         if(this.isLocked() || rowIndex < 0 ){
23927             return;
23928         };
23929         if(e.shiftKey && this.last !== false){
23930             var last = this.last;
23931             this.selectRange(last, rowIndex, e.ctrlKey);
23932             this.last = last; // reset the last
23933             t.focus();
23934     
23935         }else{
23936             var isSelected = this.isSelected(rowIndex);
23937             //Roo.log("select row:" + rowIndex);
23938             if(isSelected){
23939                 this.deselectRow(rowIndex);
23940             } else {
23941                         this.selectRow(rowIndex, true);
23942             }
23943     
23944             /*
23945                 if(e.button !== 0 && isSelected){
23946                 alert('rowIndex 2: ' + rowIndex);
23947                     view.focusRow(rowIndex);
23948                 }else if(e.ctrlKey && isSelected){
23949                     this.deselectRow(rowIndex);
23950                 }else if(!isSelected){
23951                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23952                     view.focusRow(rowIndex);
23953                 }
23954             */
23955         }
23956         this.fireEvent("afterselectionchange", this);
23957     },
23958     // private
23959     handleDragableRowClick :  function(grid, rowIndex, e) 
23960     {
23961         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23962             this.selectRow(rowIndex, false);
23963             grid.view.focusRow(rowIndex);
23964              this.fireEvent("afterselectionchange", this);
23965         }
23966     },
23967     
23968     /**
23969      * Selects multiple rows.
23970      * @param {Array} rows Array of the indexes of the row to select
23971      * @param {Boolean} keepExisting (optional) True to keep existing selections
23972      */
23973     selectRows : function(rows, keepExisting){
23974         if(!keepExisting){
23975             this.clearSelections();
23976         }
23977         for(var i = 0, len = rows.length; i < len; i++){
23978             this.selectRow(rows[i], true);
23979         }
23980     },
23981
23982     /**
23983      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23984      * @param {Number} startRow The index of the first row in the range
23985      * @param {Number} endRow The index of the last row in the range
23986      * @param {Boolean} keepExisting (optional) True to retain existing selections
23987      */
23988     selectRange : function(startRow, endRow, keepExisting){
23989         if(this.locked) {
23990             return;
23991         }
23992         if(!keepExisting){
23993             this.clearSelections();
23994         }
23995         if(startRow <= endRow){
23996             for(var i = startRow; i <= endRow; i++){
23997                 this.selectRow(i, true);
23998             }
23999         }else{
24000             for(var i = startRow; i >= endRow; i--){
24001                 this.selectRow(i, true);
24002             }
24003         }
24004     },
24005
24006     /**
24007      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24008      * @param {Number} startRow The index of the first row in the range
24009      * @param {Number} endRow The index of the last row in the range
24010      */
24011     deselectRange : function(startRow, endRow, preventViewNotify){
24012         if(this.locked) {
24013             return;
24014         }
24015         for(var i = startRow; i <= endRow; i++){
24016             this.deselectRow(i, preventViewNotify);
24017         }
24018     },
24019
24020     /**
24021      * Selects a row.
24022      * @param {Number} row The index of the row to select
24023      * @param {Boolean} keepExisting (optional) True to keep existing selections
24024      */
24025     selectRow : function(index, keepExisting, preventViewNotify)
24026     {
24027             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24028             return;
24029         }
24030         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24031             if(!keepExisting || this.singleSelect){
24032                 this.clearSelections();
24033             }
24034             
24035             var r = this.grid.store.getAt(index);
24036             //console.log('selectRow - record id :' + r.id);
24037             
24038             this.selections.add(r);
24039             this.last = this.lastActive = index;
24040             if(!preventViewNotify){
24041                 var proxy = new Roo.Element(
24042                                 this.grid.getRowDom(index)
24043                 );
24044                 proxy.addClass('bg-info info');
24045             }
24046             this.fireEvent("rowselect", this, index, r);
24047             this.fireEvent("selectionchange", this);
24048         }
24049     },
24050
24051     /**
24052      * Deselects a row.
24053      * @param {Number} row The index of the row to deselect
24054      */
24055     deselectRow : function(index, preventViewNotify)
24056     {
24057         if(this.locked) {
24058             return;
24059         }
24060         if(this.last == index){
24061             this.last = false;
24062         }
24063         if(this.lastActive == index){
24064             this.lastActive = false;
24065         }
24066         
24067         var r = this.grid.store.getAt(index);
24068         if (!r) {
24069             return;
24070         }
24071         
24072         this.selections.remove(r);
24073         //.console.log('deselectRow - record id :' + r.id);
24074         if(!preventViewNotify){
24075         
24076             var proxy = new Roo.Element(
24077                 this.grid.getRowDom(index)
24078             );
24079             proxy.removeClass('bg-info info');
24080         }
24081         this.fireEvent("rowdeselect", this, index);
24082         this.fireEvent("selectionchange", this);
24083     },
24084
24085     // private
24086     restoreLast : function(){
24087         if(this._last){
24088             this.last = this._last;
24089         }
24090     },
24091
24092     // private
24093     acceptsNav : function(row, col, cm){
24094         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24095     },
24096
24097     // private
24098     onEditorKey : function(field, e){
24099         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24100         if(k == e.TAB){
24101             e.stopEvent();
24102             ed.completeEdit();
24103             if(e.shiftKey){
24104                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24105             }else{
24106                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24107             }
24108         }else if(k == e.ENTER && !e.ctrlKey){
24109             e.stopEvent();
24110             ed.completeEdit();
24111             if(e.shiftKey){
24112                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24113             }else{
24114                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24115             }
24116         }else if(k == e.ESC){
24117             ed.cancelEdit();
24118         }
24119         if(newCell){
24120             g.startEditing(newCell[0], newCell[1]);
24121         }
24122     }
24123 });
24124 /*
24125  * Based on:
24126  * Ext JS Library 1.1.1
24127  * Copyright(c) 2006-2007, Ext JS, LLC.
24128  *
24129  * Originally Released Under LGPL - original licence link has changed is not relivant.
24130  *
24131  * Fork - LGPL
24132  * <script type="text/javascript">
24133  */
24134  
24135 /**
24136  * @class Roo.bootstrap.PagingToolbar
24137  * @extends Roo.bootstrap.NavSimplebar
24138  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24139  * @constructor
24140  * Create a new PagingToolbar
24141  * @param {Object} config The config object
24142  * @param {Roo.data.Store} store
24143  */
24144 Roo.bootstrap.PagingToolbar = function(config)
24145 {
24146     // old args format still supported... - xtype is prefered..
24147         // created from xtype...
24148     
24149     this.ds = config.dataSource;
24150     
24151     if (config.store && !this.ds) {
24152         this.store= Roo.factory(config.store, Roo.data);
24153         this.ds = this.store;
24154         this.ds.xmodule = this.xmodule || false;
24155     }
24156     
24157     this.toolbarItems = [];
24158     if (config.items) {
24159         this.toolbarItems = config.items;
24160     }
24161     
24162     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24163     
24164     this.cursor = 0;
24165     
24166     if (this.ds) { 
24167         this.bind(this.ds);
24168     }
24169     
24170     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24171     
24172 };
24173
24174 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24175     /**
24176      * @cfg {Roo.data.Store} dataSource
24177      * The underlying data store providing the paged data
24178      */
24179     /**
24180      * @cfg {String/HTMLElement/Element} container
24181      * container The id or element that will contain the toolbar
24182      */
24183     /**
24184      * @cfg {Boolean} displayInfo
24185      * True to display the displayMsg (defaults to false)
24186      */
24187     /**
24188      * @cfg {Number} pageSize
24189      * The number of records to display per page (defaults to 20)
24190      */
24191     pageSize: 20,
24192     /**
24193      * @cfg {String} displayMsg
24194      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24195      */
24196     displayMsg : 'Displaying {0} - {1} of {2}',
24197     /**
24198      * @cfg {String} emptyMsg
24199      * The message to display when no records are found (defaults to "No data to display")
24200      */
24201     emptyMsg : 'No data to display',
24202     /**
24203      * Customizable piece of the default paging text (defaults to "Page")
24204      * @type String
24205      */
24206     beforePageText : "Page",
24207     /**
24208      * Customizable piece of the default paging text (defaults to "of %0")
24209      * @type String
24210      */
24211     afterPageText : "of {0}",
24212     /**
24213      * Customizable piece of the default paging text (defaults to "First Page")
24214      * @type String
24215      */
24216     firstText : "First Page",
24217     /**
24218      * Customizable piece of the default paging text (defaults to "Previous Page")
24219      * @type String
24220      */
24221     prevText : "Previous Page",
24222     /**
24223      * Customizable piece of the default paging text (defaults to "Next Page")
24224      * @type String
24225      */
24226     nextText : "Next Page",
24227     /**
24228      * Customizable piece of the default paging text (defaults to "Last Page")
24229      * @type String
24230      */
24231     lastText : "Last Page",
24232     /**
24233      * Customizable piece of the default paging text (defaults to "Refresh")
24234      * @type String
24235      */
24236     refreshText : "Refresh",
24237
24238     buttons : false,
24239     // private
24240     onRender : function(ct, position) 
24241     {
24242         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24243         this.navgroup.parentId = this.id;
24244         this.navgroup.onRender(this.el, null);
24245         // add the buttons to the navgroup
24246         
24247         if(this.displayInfo){
24248             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24249             this.displayEl = this.el.select('.x-paging-info', true).first();
24250 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24251 //            this.displayEl = navel.el.select('span',true).first();
24252         }
24253         
24254         var _this = this;
24255         
24256         if(this.buttons){
24257             Roo.each(_this.buttons, function(e){ // this might need to use render????
24258                Roo.factory(e).onRender(_this.el, null);
24259             });
24260         }
24261             
24262         Roo.each(_this.toolbarItems, function(e) {
24263             _this.navgroup.addItem(e);
24264         });
24265         
24266         
24267         this.first = this.navgroup.addItem({
24268             tooltip: this.firstText,
24269             cls: "prev",
24270             icon : 'fa fa-backward',
24271             disabled: true,
24272             preventDefault: true,
24273             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24274         });
24275         
24276         this.prev =  this.navgroup.addItem({
24277             tooltip: this.prevText,
24278             cls: "prev",
24279             icon : 'fa fa-step-backward',
24280             disabled: true,
24281             preventDefault: true,
24282             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24283         });
24284     //this.addSeparator();
24285         
24286         
24287         var field = this.navgroup.addItem( {
24288             tagtype : 'span',
24289             cls : 'x-paging-position',
24290             
24291             html : this.beforePageText  +
24292                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24293                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24294          } ); //?? escaped?
24295         
24296         this.field = field.el.select('input', true).first();
24297         this.field.on("keydown", this.onPagingKeydown, this);
24298         this.field.on("focus", function(){this.dom.select();});
24299     
24300     
24301         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24302         //this.field.setHeight(18);
24303         //this.addSeparator();
24304         this.next = this.navgroup.addItem({
24305             tooltip: this.nextText,
24306             cls: "next",
24307             html : ' <i class="fa fa-step-forward">',
24308             disabled: true,
24309             preventDefault: true,
24310             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24311         });
24312         this.last = this.navgroup.addItem({
24313             tooltip: this.lastText,
24314             icon : 'fa fa-forward',
24315             cls: "next",
24316             disabled: true,
24317             preventDefault: true,
24318             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24319         });
24320     //this.addSeparator();
24321         this.loading = this.navgroup.addItem({
24322             tooltip: this.refreshText,
24323             icon: 'fa fa-refresh',
24324             preventDefault: true,
24325             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24326         });
24327         
24328     },
24329
24330     // private
24331     updateInfo : function(){
24332         if(this.displayEl){
24333             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24334             var msg = count == 0 ?
24335                 this.emptyMsg :
24336                 String.format(
24337                     this.displayMsg,
24338                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24339                 );
24340             this.displayEl.update(msg);
24341         }
24342     },
24343
24344     // private
24345     onLoad : function(ds, r, o)
24346     {
24347         this.cursor = o.params ? o.params.start : 0;
24348         var d = this.getPageData(),
24349             ap = d.activePage,
24350             ps = d.pages;
24351         
24352         
24353         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24354         this.field.dom.value = ap;
24355         this.first.setDisabled(ap == 1);
24356         this.prev.setDisabled(ap == 1);
24357         this.next.setDisabled(ap == ps);
24358         this.last.setDisabled(ap == ps);
24359         this.loading.enable();
24360         this.updateInfo();
24361     },
24362
24363     // private
24364     getPageData : function(){
24365         var total = this.ds.getTotalCount();
24366         return {
24367             total : total,
24368             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24369             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24370         };
24371     },
24372
24373     // private
24374     onLoadError : function(){
24375         this.loading.enable();
24376     },
24377
24378     // private
24379     onPagingKeydown : function(e){
24380         var k = e.getKey();
24381         var d = this.getPageData();
24382         if(k == e.RETURN){
24383             var v = this.field.dom.value, pageNum;
24384             if(!v || isNaN(pageNum = parseInt(v, 10))){
24385                 this.field.dom.value = d.activePage;
24386                 return;
24387             }
24388             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24389             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24390             e.stopEvent();
24391         }
24392         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))
24393         {
24394           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24395           this.field.dom.value = pageNum;
24396           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24397           e.stopEvent();
24398         }
24399         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24400         {
24401           var v = this.field.dom.value, pageNum; 
24402           var increment = (e.shiftKey) ? 10 : 1;
24403           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24404                 increment *= -1;
24405           }
24406           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24407             this.field.dom.value = d.activePage;
24408             return;
24409           }
24410           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24411           {
24412             this.field.dom.value = parseInt(v, 10) + increment;
24413             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24414             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24415           }
24416           e.stopEvent();
24417         }
24418     },
24419
24420     // private
24421     beforeLoad : function(){
24422         if(this.loading){
24423             this.loading.disable();
24424         }
24425     },
24426
24427     // private
24428     onClick : function(which){
24429         
24430         var ds = this.ds;
24431         if (!ds) {
24432             return;
24433         }
24434         
24435         switch(which){
24436             case "first":
24437                 ds.load({params:{start: 0, limit: this.pageSize}});
24438             break;
24439             case "prev":
24440                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24441             break;
24442             case "next":
24443                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24444             break;
24445             case "last":
24446                 var total = ds.getTotalCount();
24447                 var extra = total % this.pageSize;
24448                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24449                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24450             break;
24451             case "refresh":
24452                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24453             break;
24454         }
24455     },
24456
24457     /**
24458      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24459      * @param {Roo.data.Store} store The data store to unbind
24460      */
24461     unbind : function(ds){
24462         ds.un("beforeload", this.beforeLoad, this);
24463         ds.un("load", this.onLoad, this);
24464         ds.un("loadexception", this.onLoadError, this);
24465         ds.un("remove", this.updateInfo, this);
24466         ds.un("add", this.updateInfo, this);
24467         this.ds = undefined;
24468     },
24469
24470     /**
24471      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24472      * @param {Roo.data.Store} store The data store to bind
24473      */
24474     bind : function(ds){
24475         ds.on("beforeload", this.beforeLoad, this);
24476         ds.on("load", this.onLoad, this);
24477         ds.on("loadexception", this.onLoadError, this);
24478         ds.on("remove", this.updateInfo, this);
24479         ds.on("add", this.updateInfo, this);
24480         this.ds = ds;
24481     }
24482 });/*
24483  * - LGPL
24484  *
24485  * element
24486  * 
24487  */
24488
24489 /**
24490  * @class Roo.bootstrap.MessageBar
24491  * @extends Roo.bootstrap.Component
24492  * Bootstrap MessageBar class
24493  * @cfg {String} html contents of the MessageBar
24494  * @cfg {String} weight (info | success | warning | danger) default info
24495  * @cfg {String} beforeClass insert the bar before the given class
24496  * @cfg {Boolean} closable (true | false) default false
24497  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24498  * 
24499  * @constructor
24500  * Create a new Element
24501  * @param {Object} config The config object
24502  */
24503
24504 Roo.bootstrap.MessageBar = function(config){
24505     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24506 };
24507
24508 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24509     
24510     html: '',
24511     weight: 'info',
24512     closable: false,
24513     fixed: false,
24514     beforeClass: 'bootstrap-sticky-wrap',
24515     
24516     getAutoCreate : function(){
24517         
24518         var cfg = {
24519             tag: 'div',
24520             cls: 'alert alert-dismissable alert-' + this.weight,
24521             cn: [
24522                 {
24523                     tag: 'span',
24524                     cls: 'message',
24525                     html: this.html || ''
24526                 }
24527             ]
24528         };
24529         
24530         if(this.fixed){
24531             cfg.cls += ' alert-messages-fixed';
24532         }
24533         
24534         if(this.closable){
24535             cfg.cn.push({
24536                 tag: 'button',
24537                 cls: 'close',
24538                 html: 'x'
24539             });
24540         }
24541         
24542         return cfg;
24543     },
24544     
24545     onRender : function(ct, position)
24546     {
24547         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24548         
24549         if(!this.el){
24550             var cfg = Roo.apply({},  this.getAutoCreate());
24551             cfg.id = Roo.id();
24552             
24553             if (this.cls) {
24554                 cfg.cls += ' ' + this.cls;
24555             }
24556             if (this.style) {
24557                 cfg.style = this.style;
24558             }
24559             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24560             
24561             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24562         }
24563         
24564         this.el.select('>button.close').on('click', this.hide, this);
24565         
24566     },
24567     
24568     show : function()
24569     {
24570         if (!this.rendered) {
24571             this.render();
24572         }
24573         
24574         this.el.show();
24575         
24576         this.fireEvent('show', this);
24577         
24578     },
24579     
24580     hide : function()
24581     {
24582         if (!this.rendered) {
24583             this.render();
24584         }
24585         
24586         this.el.hide();
24587         
24588         this.fireEvent('hide', this);
24589     },
24590     
24591     update : function()
24592     {
24593 //        var e = this.el.dom.firstChild;
24594 //        
24595 //        if(this.closable){
24596 //            e = e.nextSibling;
24597 //        }
24598 //        
24599 //        e.data = this.html || '';
24600
24601         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24602     }
24603    
24604 });
24605
24606  
24607
24608      /*
24609  * - LGPL
24610  *
24611  * Graph
24612  * 
24613  */
24614
24615
24616 /**
24617  * @class Roo.bootstrap.Graph
24618  * @extends Roo.bootstrap.Component
24619  * Bootstrap Graph class
24620 > Prameters
24621  -sm {number} sm 4
24622  -md {number} md 5
24623  @cfg {String} graphtype  bar | vbar | pie
24624  @cfg {number} g_x coodinator | centre x (pie)
24625  @cfg {number} g_y coodinator | centre y (pie)
24626  @cfg {number} g_r radius (pie)
24627  @cfg {number} g_height height of the chart (respected by all elements in the set)
24628  @cfg {number} g_width width of the chart (respected by all elements in the set)
24629  @cfg {Object} title The title of the chart
24630     
24631  -{Array}  values
24632  -opts (object) options for the chart 
24633      o {
24634      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24635      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24636      o vgutter (number)
24637      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.
24638      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24639      o to
24640      o stretch (boolean)
24641      o }
24642  -opts (object) options for the pie
24643      o{
24644      o cut
24645      o startAngle (number)
24646      o endAngle (number)
24647      } 
24648  *
24649  * @constructor
24650  * Create a new Input
24651  * @param {Object} config The config object
24652  */
24653
24654 Roo.bootstrap.Graph = function(config){
24655     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24656     
24657     this.addEvents({
24658         // img events
24659         /**
24660          * @event click
24661          * The img click event for the img.
24662          * @param {Roo.EventObject} e
24663          */
24664         "click" : true
24665     });
24666 };
24667
24668 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24669     
24670     sm: 4,
24671     md: 5,
24672     graphtype: 'bar',
24673     g_height: 250,
24674     g_width: 400,
24675     g_x: 50,
24676     g_y: 50,
24677     g_r: 30,
24678     opts:{
24679         //g_colors: this.colors,
24680         g_type: 'soft',
24681         g_gutter: '20%'
24682
24683     },
24684     title : false,
24685
24686     getAutoCreate : function(){
24687         
24688         var cfg = {
24689             tag: 'div',
24690             html : null
24691         };
24692         
24693         
24694         return  cfg;
24695     },
24696
24697     onRender : function(ct,position){
24698         
24699         
24700         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24701         
24702         if (typeof(Raphael) == 'undefined') {
24703             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24704             return;
24705         }
24706         
24707         this.raphael = Raphael(this.el.dom);
24708         
24709                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24710                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24711                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24712                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24713                 /*
24714                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24715                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24716                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24717                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24718                 
24719                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24720                 r.barchart(330, 10, 300, 220, data1);
24721                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24722                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24723                 */
24724                 
24725                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24726                 // r.barchart(30, 30, 560, 250,  xdata, {
24727                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24728                 //     axis : "0 0 1 1",
24729                 //     axisxlabels :  xdata
24730                 //     //yvalues : cols,
24731                    
24732                 // });
24733 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24734 //        
24735 //        this.load(null,xdata,{
24736 //                axis : "0 0 1 1",
24737 //                axisxlabels :  xdata
24738 //                });
24739
24740     },
24741
24742     load : function(graphtype,xdata,opts)
24743     {
24744         this.raphael.clear();
24745         if(!graphtype) {
24746             graphtype = this.graphtype;
24747         }
24748         if(!opts){
24749             opts = this.opts;
24750         }
24751         var r = this.raphael,
24752             fin = function () {
24753                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24754             },
24755             fout = function () {
24756                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24757             },
24758             pfin = function() {
24759                 this.sector.stop();
24760                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24761
24762                 if (this.label) {
24763                     this.label[0].stop();
24764                     this.label[0].attr({ r: 7.5 });
24765                     this.label[1].attr({ "font-weight": 800 });
24766                 }
24767             },
24768             pfout = function() {
24769                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24770
24771                 if (this.label) {
24772                     this.label[0].animate({ r: 5 }, 500, "bounce");
24773                     this.label[1].attr({ "font-weight": 400 });
24774                 }
24775             };
24776
24777         switch(graphtype){
24778             case 'bar':
24779                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24780                 break;
24781             case 'hbar':
24782                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24783                 break;
24784             case 'pie':
24785 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24786 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24787 //            
24788                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24789                 
24790                 break;
24791
24792         }
24793         
24794         if(this.title){
24795             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24796         }
24797         
24798     },
24799     
24800     setTitle: function(o)
24801     {
24802         this.title = o;
24803     },
24804     
24805     initEvents: function() {
24806         
24807         if(!this.href){
24808             this.el.on('click', this.onClick, this);
24809         }
24810     },
24811     
24812     onClick : function(e)
24813     {
24814         Roo.log('img onclick');
24815         this.fireEvent('click', this, e);
24816     }
24817    
24818 });
24819
24820  
24821 /*
24822  * - LGPL
24823  *
24824  * numberBox
24825  * 
24826  */
24827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24828
24829 /**
24830  * @class Roo.bootstrap.dash.NumberBox
24831  * @extends Roo.bootstrap.Component
24832  * Bootstrap NumberBox class
24833  * @cfg {String} headline Box headline
24834  * @cfg {String} content Box content
24835  * @cfg {String} icon Box icon
24836  * @cfg {String} footer Footer text
24837  * @cfg {String} fhref Footer href
24838  * 
24839  * @constructor
24840  * Create a new NumberBox
24841  * @param {Object} config The config object
24842  */
24843
24844
24845 Roo.bootstrap.dash.NumberBox = function(config){
24846     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24847     
24848 };
24849
24850 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24851     
24852     headline : '',
24853     content : '',
24854     icon : '',
24855     footer : '',
24856     fhref : '',
24857     ficon : '',
24858     
24859     getAutoCreate : function(){
24860         
24861         var cfg = {
24862             tag : 'div',
24863             cls : 'small-box ',
24864             cn : [
24865                 {
24866                     tag : 'div',
24867                     cls : 'inner',
24868                     cn :[
24869                         {
24870                             tag : 'h3',
24871                             cls : 'roo-headline',
24872                             html : this.headline
24873                         },
24874                         {
24875                             tag : 'p',
24876                             cls : 'roo-content',
24877                             html : this.content
24878                         }
24879                     ]
24880                 }
24881             ]
24882         };
24883         
24884         if(this.icon){
24885             cfg.cn.push({
24886                 tag : 'div',
24887                 cls : 'icon',
24888                 cn :[
24889                     {
24890                         tag : 'i',
24891                         cls : 'ion ' + this.icon
24892                     }
24893                 ]
24894             });
24895         }
24896         
24897         if(this.footer){
24898             var footer = {
24899                 tag : 'a',
24900                 cls : 'small-box-footer',
24901                 href : this.fhref || '#',
24902                 html : this.footer
24903             };
24904             
24905             cfg.cn.push(footer);
24906             
24907         }
24908         
24909         return  cfg;
24910     },
24911
24912     onRender : function(ct,position){
24913         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24914
24915
24916        
24917                 
24918     },
24919
24920     setHeadline: function (value)
24921     {
24922         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24923     },
24924     
24925     setFooter: function (value, href)
24926     {
24927         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24928         
24929         if(href){
24930             this.el.select('a.small-box-footer',true).first().attr('href', href);
24931         }
24932         
24933     },
24934
24935     setContent: function (value)
24936     {
24937         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24938     },
24939
24940     initEvents: function() 
24941     {   
24942         
24943     }
24944     
24945 });
24946
24947  
24948 /*
24949  * - LGPL
24950  *
24951  * TabBox
24952  * 
24953  */
24954 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24955
24956 /**
24957  * @class Roo.bootstrap.dash.TabBox
24958  * @extends Roo.bootstrap.Component
24959  * Bootstrap TabBox class
24960  * @cfg {String} title Title of the TabBox
24961  * @cfg {String} icon Icon of the TabBox
24962  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24963  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24964  * 
24965  * @constructor
24966  * Create a new TabBox
24967  * @param {Object} config The config object
24968  */
24969
24970
24971 Roo.bootstrap.dash.TabBox = function(config){
24972     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24973     this.addEvents({
24974         // raw events
24975         /**
24976          * @event addpane
24977          * When a pane is added
24978          * @param {Roo.bootstrap.dash.TabPane} pane
24979          */
24980         "addpane" : true,
24981         /**
24982          * @event activatepane
24983          * When a pane is activated
24984          * @param {Roo.bootstrap.dash.TabPane} pane
24985          */
24986         "activatepane" : true
24987         
24988          
24989     });
24990     
24991     this.panes = [];
24992 };
24993
24994 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24995
24996     title : '',
24997     icon : false,
24998     showtabs : true,
24999     tabScrollable : false,
25000     
25001     getChildContainer : function()
25002     {
25003         return this.el.select('.tab-content', true).first();
25004     },
25005     
25006     getAutoCreate : function(){
25007         
25008         var header = {
25009             tag: 'li',
25010             cls: 'pull-left header',
25011             html: this.title,
25012             cn : []
25013         };
25014         
25015         if(this.icon){
25016             header.cn.push({
25017                 tag: 'i',
25018                 cls: 'fa ' + this.icon
25019             });
25020         }
25021         
25022         var h = {
25023             tag: 'ul',
25024             cls: 'nav nav-tabs pull-right',
25025             cn: [
25026                 header
25027             ]
25028         };
25029         
25030         if(this.tabScrollable){
25031             h = {
25032                 tag: 'div',
25033                 cls: 'tab-header',
25034                 cn: [
25035                     {
25036                         tag: 'ul',
25037                         cls: 'nav nav-tabs pull-right',
25038                         cn: [
25039                             header
25040                         ]
25041                     }
25042                 ]
25043             };
25044         }
25045         
25046         var cfg = {
25047             tag: 'div',
25048             cls: 'nav-tabs-custom',
25049             cn: [
25050                 h,
25051                 {
25052                     tag: 'div',
25053                     cls: 'tab-content no-padding',
25054                     cn: []
25055                 }
25056             ]
25057         };
25058
25059         return  cfg;
25060     },
25061     initEvents : function()
25062     {
25063         //Roo.log('add add pane handler');
25064         this.on('addpane', this.onAddPane, this);
25065     },
25066      /**
25067      * Updates the box title
25068      * @param {String} html to set the title to.
25069      */
25070     setTitle : function(value)
25071     {
25072         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25073     },
25074     onAddPane : function(pane)
25075     {
25076         this.panes.push(pane);
25077         //Roo.log('addpane');
25078         //Roo.log(pane);
25079         // tabs are rendere left to right..
25080         if(!this.showtabs){
25081             return;
25082         }
25083         
25084         var ctr = this.el.select('.nav-tabs', true).first();
25085          
25086          
25087         var existing = ctr.select('.nav-tab',true);
25088         var qty = existing.getCount();;
25089         
25090         
25091         var tab = ctr.createChild({
25092             tag : 'li',
25093             cls : 'nav-tab' + (qty ? '' : ' active'),
25094             cn : [
25095                 {
25096                     tag : 'a',
25097                     href:'#',
25098                     html : pane.title
25099                 }
25100             ]
25101         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25102         pane.tab = tab;
25103         
25104         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25105         if (!qty) {
25106             pane.el.addClass('active');
25107         }
25108         
25109                 
25110     },
25111     onTabClick : function(ev,un,ob,pane)
25112     {
25113         //Roo.log('tab - prev default');
25114         ev.preventDefault();
25115         
25116         
25117         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25118         pane.tab.addClass('active');
25119         //Roo.log(pane.title);
25120         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25121         // technically we should have a deactivate event.. but maybe add later.
25122         // and it should not de-activate the selected tab...
25123         this.fireEvent('activatepane', pane);
25124         pane.el.addClass('active');
25125         pane.fireEvent('activate');
25126         
25127         
25128     },
25129     
25130     getActivePane : function()
25131     {
25132         var r = false;
25133         Roo.each(this.panes, function(p) {
25134             if(p.el.hasClass('active')){
25135                 r = p;
25136                 return false;
25137             }
25138             
25139             return;
25140         });
25141         
25142         return r;
25143     }
25144     
25145     
25146 });
25147
25148  
25149 /*
25150  * - LGPL
25151  *
25152  * Tab pane
25153  * 
25154  */
25155 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25156 /**
25157  * @class Roo.bootstrap.TabPane
25158  * @extends Roo.bootstrap.Component
25159  * Bootstrap TabPane class
25160  * @cfg {Boolean} active (false | true) Default false
25161  * @cfg {String} title title of panel
25162
25163  * 
25164  * @constructor
25165  * Create a new TabPane
25166  * @param {Object} config The config object
25167  */
25168
25169 Roo.bootstrap.dash.TabPane = function(config){
25170     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25171     
25172     this.addEvents({
25173         // raw events
25174         /**
25175          * @event activate
25176          * When a pane is activated
25177          * @param {Roo.bootstrap.dash.TabPane} pane
25178          */
25179         "activate" : true
25180          
25181     });
25182 };
25183
25184 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25185     
25186     active : false,
25187     title : '',
25188     
25189     // the tabBox that this is attached to.
25190     tab : false,
25191      
25192     getAutoCreate : function() 
25193     {
25194         var cfg = {
25195             tag: 'div',
25196             cls: 'tab-pane'
25197         };
25198         
25199         if(this.active){
25200             cfg.cls += ' active';
25201         }
25202         
25203         return cfg;
25204     },
25205     initEvents  : function()
25206     {
25207         //Roo.log('trigger add pane handler');
25208         this.parent().fireEvent('addpane', this)
25209     },
25210     
25211      /**
25212      * Updates the tab title 
25213      * @param {String} html to set the title to.
25214      */
25215     setTitle: function(str)
25216     {
25217         if (!this.tab) {
25218             return;
25219         }
25220         this.title = str;
25221         this.tab.select('a', true).first().dom.innerHTML = str;
25222         
25223     }
25224     
25225     
25226     
25227 });
25228
25229  
25230
25231
25232  /*
25233  * - LGPL
25234  *
25235  * menu
25236  * 
25237  */
25238 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25239
25240 /**
25241  * @class Roo.bootstrap.menu.Menu
25242  * @extends Roo.bootstrap.Component
25243  * Bootstrap Menu class - container for Menu
25244  * @cfg {String} html Text of the menu
25245  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25246  * @cfg {String} icon Font awesome icon
25247  * @cfg {String} pos Menu align to (top | bottom) default bottom
25248  * 
25249  * 
25250  * @constructor
25251  * Create a new Menu
25252  * @param {Object} config The config object
25253  */
25254
25255
25256 Roo.bootstrap.menu.Menu = function(config){
25257     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25258     
25259     this.addEvents({
25260         /**
25261          * @event beforeshow
25262          * Fires before this menu is displayed
25263          * @param {Roo.bootstrap.menu.Menu} this
25264          */
25265         beforeshow : true,
25266         /**
25267          * @event beforehide
25268          * Fires before this menu is hidden
25269          * @param {Roo.bootstrap.menu.Menu} this
25270          */
25271         beforehide : true,
25272         /**
25273          * @event show
25274          * Fires after this menu is displayed
25275          * @param {Roo.bootstrap.menu.Menu} this
25276          */
25277         show : true,
25278         /**
25279          * @event hide
25280          * Fires after this menu is hidden
25281          * @param {Roo.bootstrap.menu.Menu} this
25282          */
25283         hide : true,
25284         /**
25285          * @event click
25286          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25287          * @param {Roo.bootstrap.menu.Menu} this
25288          * @param {Roo.EventObject} e
25289          */
25290         click : true
25291     });
25292     
25293 };
25294
25295 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25296     
25297     submenu : false,
25298     html : '',
25299     weight : 'default',
25300     icon : false,
25301     pos : 'bottom',
25302     
25303     
25304     getChildContainer : function() {
25305         if(this.isSubMenu){
25306             return this.el;
25307         }
25308         
25309         return this.el.select('ul.dropdown-menu', true).first();  
25310     },
25311     
25312     getAutoCreate : function()
25313     {
25314         var text = [
25315             {
25316                 tag : 'span',
25317                 cls : 'roo-menu-text',
25318                 html : this.html
25319             }
25320         ];
25321         
25322         if(this.icon){
25323             text.unshift({
25324                 tag : 'i',
25325                 cls : 'fa ' + this.icon
25326             })
25327         }
25328         
25329         
25330         var cfg = {
25331             tag : 'div',
25332             cls : 'btn-group',
25333             cn : [
25334                 {
25335                     tag : 'button',
25336                     cls : 'dropdown-button btn btn-' + this.weight,
25337                     cn : text
25338                 },
25339                 {
25340                     tag : 'button',
25341                     cls : 'dropdown-toggle btn btn-' + this.weight,
25342                     cn : [
25343                         {
25344                             tag : 'span',
25345                             cls : 'caret'
25346                         }
25347                     ]
25348                 },
25349                 {
25350                     tag : 'ul',
25351                     cls : 'dropdown-menu'
25352                 }
25353             ]
25354             
25355         };
25356         
25357         if(this.pos == 'top'){
25358             cfg.cls += ' dropup';
25359         }
25360         
25361         if(this.isSubMenu){
25362             cfg = {
25363                 tag : 'ul',
25364                 cls : 'dropdown-menu'
25365             }
25366         }
25367         
25368         return cfg;
25369     },
25370     
25371     onRender : function(ct, position)
25372     {
25373         this.isSubMenu = ct.hasClass('dropdown-submenu');
25374         
25375         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25376     },
25377     
25378     initEvents : function() 
25379     {
25380         if(this.isSubMenu){
25381             return;
25382         }
25383         
25384         this.hidden = true;
25385         
25386         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25387         this.triggerEl.on('click', this.onTriggerPress, this);
25388         
25389         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25390         this.buttonEl.on('click', this.onClick, this);
25391         
25392     },
25393     
25394     list : function()
25395     {
25396         if(this.isSubMenu){
25397             return this.el;
25398         }
25399         
25400         return this.el.select('ul.dropdown-menu', true).first();
25401     },
25402     
25403     onClick : function(e)
25404     {
25405         this.fireEvent("click", this, e);
25406     },
25407     
25408     onTriggerPress  : function(e)
25409     {   
25410         if (this.isVisible()) {
25411             this.hide();
25412         } else {
25413             this.show();
25414         }
25415     },
25416     
25417     isVisible : function(){
25418         return !this.hidden;
25419     },
25420     
25421     show : function()
25422     {
25423         this.fireEvent("beforeshow", this);
25424         
25425         this.hidden = false;
25426         this.el.addClass('open');
25427         
25428         Roo.get(document).on("mouseup", this.onMouseUp, this);
25429         
25430         this.fireEvent("show", this);
25431         
25432         
25433     },
25434     
25435     hide : function()
25436     {
25437         this.fireEvent("beforehide", this);
25438         
25439         this.hidden = true;
25440         this.el.removeClass('open');
25441         
25442         Roo.get(document).un("mouseup", this.onMouseUp);
25443         
25444         this.fireEvent("hide", this);
25445     },
25446     
25447     onMouseUp : function()
25448     {
25449         this.hide();
25450     }
25451     
25452 });
25453
25454  
25455  /*
25456  * - LGPL
25457  *
25458  * menu item
25459  * 
25460  */
25461 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25462
25463 /**
25464  * @class Roo.bootstrap.menu.Item
25465  * @extends Roo.bootstrap.Component
25466  * Bootstrap MenuItem class
25467  * @cfg {Boolean} submenu (true | false) default false
25468  * @cfg {String} html text of the item
25469  * @cfg {String} href the link
25470  * @cfg {Boolean} disable (true | false) default false
25471  * @cfg {Boolean} preventDefault (true | false) default true
25472  * @cfg {String} icon Font awesome icon
25473  * @cfg {String} pos Submenu align to (left | right) default right 
25474  * 
25475  * 
25476  * @constructor
25477  * Create a new Item
25478  * @param {Object} config The config object
25479  */
25480
25481
25482 Roo.bootstrap.menu.Item = function(config){
25483     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25484     this.addEvents({
25485         /**
25486          * @event mouseover
25487          * Fires when the mouse is hovering over this menu
25488          * @param {Roo.bootstrap.menu.Item} this
25489          * @param {Roo.EventObject} e
25490          */
25491         mouseover : true,
25492         /**
25493          * @event mouseout
25494          * Fires when the mouse exits this menu
25495          * @param {Roo.bootstrap.menu.Item} this
25496          * @param {Roo.EventObject} e
25497          */
25498         mouseout : true,
25499         // raw events
25500         /**
25501          * @event click
25502          * The raw click event for the entire grid.
25503          * @param {Roo.EventObject} e
25504          */
25505         click : true
25506     });
25507 };
25508
25509 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25510     
25511     submenu : false,
25512     href : '',
25513     html : '',
25514     preventDefault: true,
25515     disable : false,
25516     icon : false,
25517     pos : 'right',
25518     
25519     getAutoCreate : function()
25520     {
25521         var text = [
25522             {
25523                 tag : 'span',
25524                 cls : 'roo-menu-item-text',
25525                 html : this.html
25526             }
25527         ];
25528         
25529         if(this.icon){
25530             text.unshift({
25531                 tag : 'i',
25532                 cls : 'fa ' + this.icon
25533             })
25534         }
25535         
25536         var cfg = {
25537             tag : 'li',
25538             cn : [
25539                 {
25540                     tag : 'a',
25541                     href : this.href || '#',
25542                     cn : text
25543                 }
25544             ]
25545         };
25546         
25547         if(this.disable){
25548             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25549         }
25550         
25551         if(this.submenu){
25552             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25553             
25554             if(this.pos == 'left'){
25555                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25556             }
25557         }
25558         
25559         return cfg;
25560     },
25561     
25562     initEvents : function() 
25563     {
25564         this.el.on('mouseover', this.onMouseOver, this);
25565         this.el.on('mouseout', this.onMouseOut, this);
25566         
25567         this.el.select('a', true).first().on('click', this.onClick, this);
25568         
25569     },
25570     
25571     onClick : function(e)
25572     {
25573         if(this.preventDefault){
25574             e.preventDefault();
25575         }
25576         
25577         this.fireEvent("click", this, e);
25578     },
25579     
25580     onMouseOver : function(e)
25581     {
25582         if(this.submenu && this.pos == 'left'){
25583             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25584         }
25585         
25586         this.fireEvent("mouseover", this, e);
25587     },
25588     
25589     onMouseOut : function(e)
25590     {
25591         this.fireEvent("mouseout", this, e);
25592     }
25593 });
25594
25595  
25596
25597  /*
25598  * - LGPL
25599  *
25600  * menu separator
25601  * 
25602  */
25603 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25604
25605 /**
25606  * @class Roo.bootstrap.menu.Separator
25607  * @extends Roo.bootstrap.Component
25608  * Bootstrap Separator class
25609  * 
25610  * @constructor
25611  * Create a new Separator
25612  * @param {Object} config The config object
25613  */
25614
25615
25616 Roo.bootstrap.menu.Separator = function(config){
25617     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25618 };
25619
25620 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25621     
25622     getAutoCreate : function(){
25623         var cfg = {
25624             tag : 'li',
25625             cls: 'divider'
25626         };
25627         
25628         return cfg;
25629     }
25630    
25631 });
25632
25633  
25634
25635  /*
25636  * - LGPL
25637  *
25638  * Tooltip
25639  * 
25640  */
25641
25642 /**
25643  * @class Roo.bootstrap.Tooltip
25644  * Bootstrap Tooltip class
25645  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25646  * to determine which dom element triggers the tooltip.
25647  * 
25648  * It needs to add support for additional attributes like tooltip-position
25649  * 
25650  * @constructor
25651  * Create a new Toolti
25652  * @param {Object} config The config object
25653  */
25654
25655 Roo.bootstrap.Tooltip = function(config){
25656     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25657     
25658     this.alignment = Roo.bootstrap.Tooltip.alignment;
25659     
25660     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25661         this.alignment = config.alignment;
25662     }
25663     
25664 };
25665
25666 Roo.apply(Roo.bootstrap.Tooltip, {
25667     /**
25668      * @function init initialize tooltip monitoring.
25669      * @static
25670      */
25671     currentEl : false,
25672     currentTip : false,
25673     currentRegion : false,
25674     
25675     //  init : delay?
25676     
25677     init : function()
25678     {
25679         Roo.get(document).on('mouseover', this.enter ,this);
25680         Roo.get(document).on('mouseout', this.leave, this);
25681          
25682         
25683         this.currentTip = new Roo.bootstrap.Tooltip();
25684     },
25685     
25686     enter : function(ev)
25687     {
25688         var dom = ev.getTarget();
25689         
25690         //Roo.log(['enter',dom]);
25691         var el = Roo.fly(dom);
25692         if (this.currentEl) {
25693             //Roo.log(dom);
25694             //Roo.log(this.currentEl);
25695             //Roo.log(this.currentEl.contains(dom));
25696             if (this.currentEl == el) {
25697                 return;
25698             }
25699             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25700                 return;
25701             }
25702
25703         }
25704         
25705         if (this.currentTip.el) {
25706             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25707         }    
25708         //Roo.log(ev);
25709         
25710         if(!el || el.dom == document){
25711             return;
25712         }
25713         
25714         var bindEl = el;
25715         
25716         // you can not look for children, as if el is the body.. then everythign is the child..
25717         if (!el.attr('tooltip')) { //
25718             if (!el.select("[tooltip]").elements.length) {
25719                 return;
25720             }
25721             // is the mouse over this child...?
25722             bindEl = el.select("[tooltip]").first();
25723             var xy = ev.getXY();
25724             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25725                 //Roo.log("not in region.");
25726                 return;
25727             }
25728             //Roo.log("child element over..");
25729             
25730         }
25731         this.currentEl = bindEl;
25732         this.currentTip.bind(bindEl);
25733         this.currentRegion = Roo.lib.Region.getRegion(dom);
25734         this.currentTip.enter();
25735         
25736     },
25737     leave : function(ev)
25738     {
25739         var dom = ev.getTarget();
25740         //Roo.log(['leave',dom]);
25741         if (!this.currentEl) {
25742             return;
25743         }
25744         
25745         
25746         if (dom != this.currentEl.dom) {
25747             return;
25748         }
25749         var xy = ev.getXY();
25750         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25751             return;
25752         }
25753         // only activate leave if mouse cursor is outside... bounding box..
25754         
25755         
25756         
25757         
25758         if (this.currentTip) {
25759             this.currentTip.leave();
25760         }
25761         //Roo.log('clear currentEl');
25762         this.currentEl = false;
25763         
25764         
25765     },
25766     alignment : {
25767         'left' : ['r-l', [-2,0], 'right'],
25768         'right' : ['l-r', [2,0], 'left'],
25769         'bottom' : ['t-b', [0,2], 'top'],
25770         'top' : [ 'b-t', [0,-2], 'bottom']
25771     }
25772     
25773 });
25774
25775
25776 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25777     
25778     
25779     bindEl : false,
25780     
25781     delay : null, // can be { show : 300 , hide: 500}
25782     
25783     timeout : null,
25784     
25785     hoverState : null, //???
25786     
25787     placement : 'bottom', 
25788     
25789     alignment : false,
25790     
25791     getAutoCreate : function(){
25792     
25793         var cfg = {
25794            cls : 'tooltip',
25795            role : 'tooltip',
25796            cn : [
25797                 {
25798                     cls : 'tooltip-arrow'
25799                 },
25800                 {
25801                     cls : 'tooltip-inner'
25802                 }
25803            ]
25804         };
25805         
25806         return cfg;
25807     },
25808     bind : function(el)
25809     {
25810         this.bindEl = el;
25811     },
25812       
25813     
25814     enter : function () {
25815        
25816         if (this.timeout != null) {
25817             clearTimeout(this.timeout);
25818         }
25819         
25820         this.hoverState = 'in';
25821          //Roo.log("enter - show");
25822         if (!this.delay || !this.delay.show) {
25823             this.show();
25824             return;
25825         }
25826         var _t = this;
25827         this.timeout = setTimeout(function () {
25828             if (_t.hoverState == 'in') {
25829                 _t.show();
25830             }
25831         }, this.delay.show);
25832     },
25833     leave : function()
25834     {
25835         clearTimeout(this.timeout);
25836     
25837         this.hoverState = 'out';
25838          if (!this.delay || !this.delay.hide) {
25839             this.hide();
25840             return;
25841         }
25842        
25843         var _t = this;
25844         this.timeout = setTimeout(function () {
25845             //Roo.log("leave - timeout");
25846             
25847             if (_t.hoverState == 'out') {
25848                 _t.hide();
25849                 Roo.bootstrap.Tooltip.currentEl = false;
25850             }
25851         }, delay);
25852     },
25853     
25854     show : function (msg)
25855     {
25856         if (!this.el) {
25857             this.render(document.body);
25858         }
25859         // set content.
25860         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25861         
25862         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25863         
25864         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25865         
25866         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25867         
25868         var placement = typeof this.placement == 'function' ?
25869             this.placement.call(this, this.el, on_el) :
25870             this.placement;
25871             
25872         var autoToken = /\s?auto?\s?/i;
25873         var autoPlace = autoToken.test(placement);
25874         if (autoPlace) {
25875             placement = placement.replace(autoToken, '') || 'top';
25876         }
25877         
25878         //this.el.detach()
25879         //this.el.setXY([0,0]);
25880         this.el.show();
25881         //this.el.dom.style.display='block';
25882         
25883         //this.el.appendTo(on_el);
25884         
25885         var p = this.getPosition();
25886         var box = this.el.getBox();
25887         
25888         if (autoPlace) {
25889             // fixme..
25890         }
25891         
25892         var align = this.alignment[placement];
25893         
25894         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25895         
25896         if(placement == 'top' || placement == 'bottom'){
25897             if(xy[0] < 0){
25898                 placement = 'right';
25899             }
25900             
25901             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25902                 placement = 'left';
25903             }
25904             
25905             var scroll = Roo.select('body', true).first().getScroll();
25906             
25907             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25908                 placement = 'top';
25909             }
25910             
25911         }
25912         
25913         this.el.alignTo(this.bindEl, align[0],align[1]);
25914         //var arrow = this.el.select('.arrow',true).first();
25915         //arrow.set(align[2], 
25916         
25917         this.el.addClass(placement);
25918         
25919         this.el.addClass('in fade');
25920         
25921         this.hoverState = null;
25922         
25923         if (this.el.hasClass('fade')) {
25924             // fade it?
25925         }
25926         
25927     },
25928     hide : function()
25929     {
25930          
25931         if (!this.el) {
25932             return;
25933         }
25934         //this.el.setXY([0,0]);
25935         this.el.removeClass('in');
25936         //this.el.hide();
25937         
25938     }
25939     
25940 });
25941  
25942
25943  /*
25944  * - LGPL
25945  *
25946  * Location Picker
25947  * 
25948  */
25949
25950 /**
25951  * @class Roo.bootstrap.LocationPicker
25952  * @extends Roo.bootstrap.Component
25953  * Bootstrap LocationPicker class
25954  * @cfg {Number} latitude Position when init default 0
25955  * @cfg {Number} longitude Position when init default 0
25956  * @cfg {Number} zoom default 15
25957  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25958  * @cfg {Boolean} mapTypeControl default false
25959  * @cfg {Boolean} disableDoubleClickZoom default false
25960  * @cfg {Boolean} scrollwheel default true
25961  * @cfg {Boolean} streetViewControl default false
25962  * @cfg {Number} radius default 0
25963  * @cfg {String} locationName
25964  * @cfg {Boolean} draggable default true
25965  * @cfg {Boolean} enableAutocomplete default false
25966  * @cfg {Boolean} enableReverseGeocode default true
25967  * @cfg {String} markerTitle
25968  * 
25969  * @constructor
25970  * Create a new LocationPicker
25971  * @param {Object} config The config object
25972  */
25973
25974
25975 Roo.bootstrap.LocationPicker = function(config){
25976     
25977     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25978     
25979     this.addEvents({
25980         /**
25981          * @event initial
25982          * Fires when the picker initialized.
25983          * @param {Roo.bootstrap.LocationPicker} this
25984          * @param {Google Location} location
25985          */
25986         initial : true,
25987         /**
25988          * @event positionchanged
25989          * Fires when the picker position changed.
25990          * @param {Roo.bootstrap.LocationPicker} this
25991          * @param {Google Location} location
25992          */
25993         positionchanged : true,
25994         /**
25995          * @event resize
25996          * Fires when the map resize.
25997          * @param {Roo.bootstrap.LocationPicker} this
25998          */
25999         resize : true,
26000         /**
26001          * @event show
26002          * Fires when the map show.
26003          * @param {Roo.bootstrap.LocationPicker} this
26004          */
26005         show : true,
26006         /**
26007          * @event hide
26008          * Fires when the map hide.
26009          * @param {Roo.bootstrap.LocationPicker} this
26010          */
26011         hide : true,
26012         /**
26013          * @event mapClick
26014          * Fires when click the map.
26015          * @param {Roo.bootstrap.LocationPicker} this
26016          * @param {Map event} e
26017          */
26018         mapClick : true,
26019         /**
26020          * @event mapRightClick
26021          * Fires when right click the map.
26022          * @param {Roo.bootstrap.LocationPicker} this
26023          * @param {Map event} e
26024          */
26025         mapRightClick : true,
26026         /**
26027          * @event markerClick
26028          * Fires when click the marker.
26029          * @param {Roo.bootstrap.LocationPicker} this
26030          * @param {Map event} e
26031          */
26032         markerClick : true,
26033         /**
26034          * @event markerRightClick
26035          * Fires when right click the marker.
26036          * @param {Roo.bootstrap.LocationPicker} this
26037          * @param {Map event} e
26038          */
26039         markerRightClick : true,
26040         /**
26041          * @event OverlayViewDraw
26042          * Fires when OverlayView Draw
26043          * @param {Roo.bootstrap.LocationPicker} this
26044          */
26045         OverlayViewDraw : true,
26046         /**
26047          * @event OverlayViewOnAdd
26048          * Fires when OverlayView Draw
26049          * @param {Roo.bootstrap.LocationPicker} this
26050          */
26051         OverlayViewOnAdd : true,
26052         /**
26053          * @event OverlayViewOnRemove
26054          * Fires when OverlayView Draw
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          */
26057         OverlayViewOnRemove : true,
26058         /**
26059          * @event OverlayViewShow
26060          * Fires when OverlayView Draw
26061          * @param {Roo.bootstrap.LocationPicker} this
26062          * @param {Pixel} cpx
26063          */
26064         OverlayViewShow : true,
26065         /**
26066          * @event OverlayViewHide
26067          * Fires when OverlayView Draw
26068          * @param {Roo.bootstrap.LocationPicker} this
26069          */
26070         OverlayViewHide : true,
26071         /**
26072          * @event loadexception
26073          * Fires when load google lib failed.
26074          * @param {Roo.bootstrap.LocationPicker} this
26075          */
26076         loadexception : true
26077     });
26078         
26079 };
26080
26081 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26082     
26083     gMapContext: false,
26084     
26085     latitude: 0,
26086     longitude: 0,
26087     zoom: 15,
26088     mapTypeId: false,
26089     mapTypeControl: false,
26090     disableDoubleClickZoom: false,
26091     scrollwheel: true,
26092     streetViewControl: false,
26093     radius: 0,
26094     locationName: '',
26095     draggable: true,
26096     enableAutocomplete: false,
26097     enableReverseGeocode: true,
26098     markerTitle: '',
26099     
26100     getAutoCreate: function()
26101     {
26102
26103         var cfg = {
26104             tag: 'div',
26105             cls: 'roo-location-picker'
26106         };
26107         
26108         return cfg
26109     },
26110     
26111     initEvents: function(ct, position)
26112     {       
26113         if(!this.el.getWidth() || this.isApplied()){
26114             return;
26115         }
26116         
26117         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26118         
26119         this.initial();
26120     },
26121     
26122     initial: function()
26123     {
26124         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26125             this.fireEvent('loadexception', this);
26126             return;
26127         }
26128         
26129         if(!this.mapTypeId){
26130             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26131         }
26132         
26133         this.gMapContext = this.GMapContext();
26134         
26135         this.initOverlayView();
26136         
26137         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26138         
26139         var _this = this;
26140                 
26141         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26142             _this.setPosition(_this.gMapContext.marker.position);
26143         });
26144         
26145         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26146             _this.fireEvent('mapClick', this, event);
26147             
26148         });
26149
26150         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26151             _this.fireEvent('mapRightClick', this, event);
26152             
26153         });
26154         
26155         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26156             _this.fireEvent('markerClick', this, event);
26157             
26158         });
26159
26160         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26161             _this.fireEvent('markerRightClick', this, event);
26162             
26163         });
26164         
26165         this.setPosition(this.gMapContext.location);
26166         
26167         this.fireEvent('initial', this, this.gMapContext.location);
26168     },
26169     
26170     initOverlayView: function()
26171     {
26172         var _this = this;
26173         
26174         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26175             
26176             draw: function()
26177             {
26178                 _this.fireEvent('OverlayViewDraw', _this);
26179             },
26180             
26181             onAdd: function()
26182             {
26183                 _this.fireEvent('OverlayViewOnAdd', _this);
26184             },
26185             
26186             onRemove: function()
26187             {
26188                 _this.fireEvent('OverlayViewOnRemove', _this);
26189             },
26190             
26191             show: function(cpx)
26192             {
26193                 _this.fireEvent('OverlayViewShow', _this, cpx);
26194             },
26195             
26196             hide: function()
26197             {
26198                 _this.fireEvent('OverlayViewHide', _this);
26199             }
26200             
26201         });
26202     },
26203     
26204     fromLatLngToContainerPixel: function(event)
26205     {
26206         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26207     },
26208     
26209     isApplied: function() 
26210     {
26211         return this.getGmapContext() == false ? false : true;
26212     },
26213     
26214     getGmapContext: function() 
26215     {
26216         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26217     },
26218     
26219     GMapContext: function() 
26220     {
26221         var position = new google.maps.LatLng(this.latitude, this.longitude);
26222         
26223         var _map = new google.maps.Map(this.el.dom, {
26224             center: position,
26225             zoom: this.zoom,
26226             mapTypeId: this.mapTypeId,
26227             mapTypeControl: this.mapTypeControl,
26228             disableDoubleClickZoom: this.disableDoubleClickZoom,
26229             scrollwheel: this.scrollwheel,
26230             streetViewControl: this.streetViewControl,
26231             locationName: this.locationName,
26232             draggable: this.draggable,
26233             enableAutocomplete: this.enableAutocomplete,
26234             enableReverseGeocode: this.enableReverseGeocode
26235         });
26236         
26237         var _marker = new google.maps.Marker({
26238             position: position,
26239             map: _map,
26240             title: this.markerTitle,
26241             draggable: this.draggable
26242         });
26243         
26244         return {
26245             map: _map,
26246             marker: _marker,
26247             circle: null,
26248             location: position,
26249             radius: this.radius,
26250             locationName: this.locationName,
26251             addressComponents: {
26252                 formatted_address: null,
26253                 addressLine1: null,
26254                 addressLine2: null,
26255                 streetName: null,
26256                 streetNumber: null,
26257                 city: null,
26258                 district: null,
26259                 state: null,
26260                 stateOrProvince: null
26261             },
26262             settings: this,
26263             domContainer: this.el.dom,
26264             geodecoder: new google.maps.Geocoder()
26265         };
26266     },
26267     
26268     drawCircle: function(center, radius, options) 
26269     {
26270         if (this.gMapContext.circle != null) {
26271             this.gMapContext.circle.setMap(null);
26272         }
26273         if (radius > 0) {
26274             radius *= 1;
26275             options = Roo.apply({}, options, {
26276                 strokeColor: "#0000FF",
26277                 strokeOpacity: .35,
26278                 strokeWeight: 2,
26279                 fillColor: "#0000FF",
26280                 fillOpacity: .2
26281             });
26282             
26283             options.map = this.gMapContext.map;
26284             options.radius = radius;
26285             options.center = center;
26286             this.gMapContext.circle = new google.maps.Circle(options);
26287             return this.gMapContext.circle;
26288         }
26289         
26290         return null;
26291     },
26292     
26293     setPosition: function(location) 
26294     {
26295         this.gMapContext.location = location;
26296         this.gMapContext.marker.setPosition(location);
26297         this.gMapContext.map.panTo(location);
26298         this.drawCircle(location, this.gMapContext.radius, {});
26299         
26300         var _this = this;
26301         
26302         if (this.gMapContext.settings.enableReverseGeocode) {
26303             this.gMapContext.geodecoder.geocode({
26304                 latLng: this.gMapContext.location
26305             }, function(results, status) {
26306                 
26307                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26308                     _this.gMapContext.locationName = results[0].formatted_address;
26309                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26310                     
26311                     _this.fireEvent('positionchanged', this, location);
26312                 }
26313             });
26314             
26315             return;
26316         }
26317         
26318         this.fireEvent('positionchanged', this, location);
26319     },
26320     
26321     resize: function()
26322     {
26323         google.maps.event.trigger(this.gMapContext.map, "resize");
26324         
26325         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26326         
26327         this.fireEvent('resize', this);
26328     },
26329     
26330     setPositionByLatLng: function(latitude, longitude)
26331     {
26332         this.setPosition(new google.maps.LatLng(latitude, longitude));
26333     },
26334     
26335     getCurrentPosition: function() 
26336     {
26337         return {
26338             latitude: this.gMapContext.location.lat(),
26339             longitude: this.gMapContext.location.lng()
26340         };
26341     },
26342     
26343     getAddressName: function() 
26344     {
26345         return this.gMapContext.locationName;
26346     },
26347     
26348     getAddressComponents: function() 
26349     {
26350         return this.gMapContext.addressComponents;
26351     },
26352     
26353     address_component_from_google_geocode: function(address_components) 
26354     {
26355         var result = {};
26356         
26357         for (var i = 0; i < address_components.length; i++) {
26358             var component = address_components[i];
26359             if (component.types.indexOf("postal_code") >= 0) {
26360                 result.postalCode = component.short_name;
26361             } else if (component.types.indexOf("street_number") >= 0) {
26362                 result.streetNumber = component.short_name;
26363             } else if (component.types.indexOf("route") >= 0) {
26364                 result.streetName = component.short_name;
26365             } else if (component.types.indexOf("neighborhood") >= 0) {
26366                 result.city = component.short_name;
26367             } else if (component.types.indexOf("locality") >= 0) {
26368                 result.city = component.short_name;
26369             } else if (component.types.indexOf("sublocality") >= 0) {
26370                 result.district = component.short_name;
26371             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26372                 result.stateOrProvince = component.short_name;
26373             } else if (component.types.indexOf("country") >= 0) {
26374                 result.country = component.short_name;
26375             }
26376         }
26377         
26378         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26379         result.addressLine2 = "";
26380         return result;
26381     },
26382     
26383     setZoomLevel: function(zoom)
26384     {
26385         this.gMapContext.map.setZoom(zoom);
26386     },
26387     
26388     show: function()
26389     {
26390         if(!this.el){
26391             return;
26392         }
26393         
26394         this.el.show();
26395         
26396         this.resize();
26397         
26398         this.fireEvent('show', this);
26399     },
26400     
26401     hide: function()
26402     {
26403         if(!this.el){
26404             return;
26405         }
26406         
26407         this.el.hide();
26408         
26409         this.fireEvent('hide', this);
26410     }
26411     
26412 });
26413
26414 Roo.apply(Roo.bootstrap.LocationPicker, {
26415     
26416     OverlayView : function(map, options)
26417     {
26418         options = options || {};
26419         
26420         this.setMap(map);
26421     }
26422     
26423     
26424 });/*
26425  * - LGPL
26426  *
26427  * Alert
26428  * 
26429  */
26430
26431 /**
26432  * @class Roo.bootstrap.Alert
26433  * @extends Roo.bootstrap.Component
26434  * Bootstrap Alert class
26435  * @cfg {String} title The title of alert
26436  * @cfg {String} html The content of alert
26437  * @cfg {String} weight (  success | info | warning | danger )
26438  * @cfg {String} faicon font-awesomeicon
26439  * 
26440  * @constructor
26441  * Create a new alert
26442  * @param {Object} config The config object
26443  */
26444
26445
26446 Roo.bootstrap.Alert = function(config){
26447     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26448     
26449 };
26450
26451 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26452     
26453     title: '',
26454     html: '',
26455     weight: false,
26456     faicon: false,
26457     
26458     getAutoCreate : function()
26459     {
26460         
26461         var cfg = {
26462             tag : 'div',
26463             cls : 'alert',
26464             cn : [
26465                 {
26466                     tag : 'i',
26467                     cls : 'roo-alert-icon'
26468                     
26469                 },
26470                 {
26471                     tag : 'b',
26472                     cls : 'roo-alert-title',
26473                     html : this.title
26474                 },
26475                 {
26476                     tag : 'span',
26477                     cls : 'roo-alert-text',
26478                     html : this.html
26479                 }
26480             ]
26481         };
26482         
26483         if(this.faicon){
26484             cfg.cn[0].cls += ' fa ' + this.faicon;
26485         }
26486         
26487         if(this.weight){
26488             cfg.cls += ' alert-' + this.weight;
26489         }
26490         
26491         return cfg;
26492     },
26493     
26494     initEvents: function() 
26495     {
26496         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26497     },
26498     
26499     setTitle : function(str)
26500     {
26501         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26502     },
26503     
26504     setText : function(str)
26505     {
26506         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26507     },
26508     
26509     setWeight : function(weight)
26510     {
26511         if(this.weight){
26512             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26513         }
26514         
26515         this.weight = weight;
26516         
26517         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26518     },
26519     
26520     setIcon : function(icon)
26521     {
26522         if(this.faicon){
26523             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26524         }
26525         
26526         this.faicon = icon;
26527         
26528         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26529     },
26530     
26531     hide: function() 
26532     {
26533         this.el.hide();   
26534     },
26535     
26536     show: function() 
26537     {  
26538         this.el.show();   
26539     }
26540     
26541 });
26542
26543  
26544 /*
26545 * Licence: LGPL
26546 */
26547
26548 /**
26549  * @class Roo.bootstrap.UploadCropbox
26550  * @extends Roo.bootstrap.Component
26551  * Bootstrap UploadCropbox class
26552  * @cfg {String} emptyText show when image has been loaded
26553  * @cfg {String} rotateNotify show when image too small to rotate
26554  * @cfg {Number} errorTimeout default 3000
26555  * @cfg {Number} minWidth default 300
26556  * @cfg {Number} minHeight default 300
26557  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26558  * @cfg {Boolean} isDocument (true|false) default false
26559  * @cfg {String} url action url
26560  * @cfg {String} paramName default 'imageUpload'
26561  * @cfg {String} method default POST
26562  * @cfg {Boolean} loadMask (true|false) default true
26563  * @cfg {Boolean} loadingText default 'Loading...'
26564  * 
26565  * @constructor
26566  * Create a new UploadCropbox
26567  * @param {Object} config The config object
26568  */
26569
26570 Roo.bootstrap.UploadCropbox = function(config){
26571     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26572     
26573     this.addEvents({
26574         /**
26575          * @event beforeselectfile
26576          * Fire before select file
26577          * @param {Roo.bootstrap.UploadCropbox} this
26578          */
26579         "beforeselectfile" : true,
26580         /**
26581          * @event initial
26582          * Fire after initEvent
26583          * @param {Roo.bootstrap.UploadCropbox} this
26584          */
26585         "initial" : true,
26586         /**
26587          * @event crop
26588          * Fire after initEvent
26589          * @param {Roo.bootstrap.UploadCropbox} this
26590          * @param {String} data
26591          */
26592         "crop" : true,
26593         /**
26594          * @event prepare
26595          * Fire when preparing the file data
26596          * @param {Roo.bootstrap.UploadCropbox} this
26597          * @param {Object} file
26598          */
26599         "prepare" : true,
26600         /**
26601          * @event exception
26602          * Fire when get exception
26603          * @param {Roo.bootstrap.UploadCropbox} this
26604          * @param {XMLHttpRequest} xhr
26605          */
26606         "exception" : true,
26607         /**
26608          * @event beforeloadcanvas
26609          * Fire before load the canvas
26610          * @param {Roo.bootstrap.UploadCropbox} this
26611          * @param {String} src
26612          */
26613         "beforeloadcanvas" : true,
26614         /**
26615          * @event trash
26616          * Fire when trash image
26617          * @param {Roo.bootstrap.UploadCropbox} this
26618          */
26619         "trash" : true,
26620         /**
26621          * @event download
26622          * Fire when download the image
26623          * @param {Roo.bootstrap.UploadCropbox} this
26624          */
26625         "download" : true,
26626         /**
26627          * @event footerbuttonclick
26628          * Fire when footerbuttonclick
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          * @param {String} type
26631          */
26632         "footerbuttonclick" : true,
26633         /**
26634          * @event resize
26635          * Fire when resize
26636          * @param {Roo.bootstrap.UploadCropbox} this
26637          */
26638         "resize" : true,
26639         /**
26640          * @event rotate
26641          * Fire when rotate the image
26642          * @param {Roo.bootstrap.UploadCropbox} this
26643          * @param {String} pos
26644          */
26645         "rotate" : true,
26646         /**
26647          * @event inspect
26648          * Fire when inspect the file
26649          * @param {Roo.bootstrap.UploadCropbox} this
26650          * @param {Object} file
26651          */
26652         "inspect" : true,
26653         /**
26654          * @event upload
26655          * Fire when xhr upload the file
26656          * @param {Roo.bootstrap.UploadCropbox} this
26657          * @param {Object} data
26658          */
26659         "upload" : true,
26660         /**
26661          * @event arrange
26662          * Fire when arrange the file data
26663          * @param {Roo.bootstrap.UploadCropbox} this
26664          * @param {Object} formData
26665          */
26666         "arrange" : true
26667     });
26668     
26669     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26670 };
26671
26672 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26673     
26674     emptyText : 'Click to upload image',
26675     rotateNotify : 'Image is too small to rotate',
26676     errorTimeout : 3000,
26677     scale : 0,
26678     baseScale : 1,
26679     rotate : 0,
26680     dragable : false,
26681     pinching : false,
26682     mouseX : 0,
26683     mouseY : 0,
26684     cropData : false,
26685     minWidth : 300,
26686     minHeight : 300,
26687     file : false,
26688     exif : {},
26689     baseRotate : 1,
26690     cropType : 'image/jpeg',
26691     buttons : false,
26692     canvasLoaded : false,
26693     isDocument : false,
26694     method : 'POST',
26695     paramName : 'imageUpload',
26696     loadMask : true,
26697     loadingText : 'Loading...',
26698     maskEl : false,
26699     
26700     getAutoCreate : function()
26701     {
26702         var cfg = {
26703             tag : 'div',
26704             cls : 'roo-upload-cropbox',
26705             cn : [
26706                 {
26707                     tag : 'input',
26708                     cls : 'roo-upload-cropbox-selector',
26709                     type : 'file'
26710                 },
26711                 {
26712                     tag : 'div',
26713                     cls : 'roo-upload-cropbox-body',
26714                     style : 'cursor:pointer',
26715                     cn : [
26716                         {
26717                             tag : 'div',
26718                             cls : 'roo-upload-cropbox-preview'
26719                         },
26720                         {
26721                             tag : 'div',
26722                             cls : 'roo-upload-cropbox-thumb'
26723                         },
26724                         {
26725                             tag : 'div',
26726                             cls : 'roo-upload-cropbox-empty-notify',
26727                             html : this.emptyText
26728                         },
26729                         {
26730                             tag : 'div',
26731                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26732                             html : this.rotateNotify
26733                         }
26734                     ]
26735                 },
26736                 {
26737                     tag : 'div',
26738                     cls : 'roo-upload-cropbox-footer',
26739                     cn : {
26740                         tag : 'div',
26741                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26742                         cn : []
26743                     }
26744                 }
26745             ]
26746         };
26747         
26748         return cfg;
26749     },
26750     
26751     onRender : function(ct, position)
26752     {
26753         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26754         
26755         if (this.buttons.length) {
26756             
26757             Roo.each(this.buttons, function(bb) {
26758                 
26759                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26760                 
26761                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26762                 
26763             }, this);
26764         }
26765         
26766         if(this.loadMask){
26767             this.maskEl = this.el;
26768         }
26769     },
26770     
26771     initEvents : function()
26772     {
26773         this.urlAPI = (window.createObjectURL && window) || 
26774                                 (window.URL && URL.revokeObjectURL && URL) || 
26775                                 (window.webkitURL && webkitURL);
26776                         
26777         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26778         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26779         
26780         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26781         this.selectorEl.hide();
26782         
26783         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26784         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26785         
26786         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26787         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26788         this.thumbEl.hide();
26789         
26790         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26791         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26792         
26793         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26794         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26795         this.errorEl.hide();
26796         
26797         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26798         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26799         this.footerEl.hide();
26800         
26801         this.setThumbBoxSize();
26802         
26803         this.bind();
26804         
26805         this.resize();
26806         
26807         this.fireEvent('initial', this);
26808     },
26809
26810     bind : function()
26811     {
26812         var _this = this;
26813         
26814         window.addEventListener("resize", function() { _this.resize(); } );
26815         
26816         this.bodyEl.on('click', this.beforeSelectFile, this);
26817         
26818         if(Roo.isTouch){
26819             this.bodyEl.on('touchstart', this.onTouchStart, this);
26820             this.bodyEl.on('touchmove', this.onTouchMove, this);
26821             this.bodyEl.on('touchend', this.onTouchEnd, this);
26822         }
26823         
26824         if(!Roo.isTouch){
26825             this.bodyEl.on('mousedown', this.onMouseDown, this);
26826             this.bodyEl.on('mousemove', this.onMouseMove, this);
26827             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26828             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26829             Roo.get(document).on('mouseup', this.onMouseUp, this);
26830         }
26831         
26832         this.selectorEl.on('change', this.onFileSelected, this);
26833     },
26834     
26835     reset : function()
26836     {    
26837         this.scale = 0;
26838         this.baseScale = 1;
26839         this.rotate = 0;
26840         this.baseRotate = 1;
26841         this.dragable = false;
26842         this.pinching = false;
26843         this.mouseX = 0;
26844         this.mouseY = 0;
26845         this.cropData = false;
26846         this.notifyEl.dom.innerHTML = this.emptyText;
26847         
26848         this.selectorEl.dom.value = '';
26849         
26850     },
26851     
26852     resize : function()
26853     {
26854         if(this.fireEvent('resize', this) != false){
26855             this.setThumbBoxPosition();
26856             this.setCanvasPosition();
26857         }
26858     },
26859     
26860     onFooterButtonClick : function(e, el, o, type)
26861     {
26862         switch (type) {
26863             case 'rotate-left' :
26864                 this.onRotateLeft(e);
26865                 break;
26866             case 'rotate-right' :
26867                 this.onRotateRight(e);
26868                 break;
26869             case 'picture' :
26870                 this.beforeSelectFile(e);
26871                 break;
26872             case 'trash' :
26873                 this.trash(e);
26874                 break;
26875             case 'crop' :
26876                 this.crop(e);
26877                 break;
26878             case 'download' :
26879                 this.download(e);
26880                 break;
26881             default :
26882                 break;
26883         }
26884         
26885         this.fireEvent('footerbuttonclick', this, type);
26886     },
26887     
26888     beforeSelectFile : function(e)
26889     {
26890         e.preventDefault();
26891         
26892         if(this.fireEvent('beforeselectfile', this) != false){
26893             this.selectorEl.dom.click();
26894         }
26895     },
26896     
26897     onFileSelected : function(e)
26898     {
26899         e.preventDefault();
26900         
26901         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26902             return;
26903         }
26904         
26905         var file = this.selectorEl.dom.files[0];
26906         
26907         if(this.fireEvent('inspect', this, file) != false){
26908             this.prepare(file);
26909         }
26910         
26911     },
26912     
26913     trash : function(e)
26914     {
26915         this.fireEvent('trash', this);
26916     },
26917     
26918     download : function(e)
26919     {
26920         this.fireEvent('download', this);
26921     },
26922     
26923     loadCanvas : function(src)
26924     {   
26925         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26926             
26927             this.reset();
26928             
26929             this.imageEl = document.createElement('img');
26930             
26931             var _this = this;
26932             
26933             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26934             
26935             this.imageEl.src = src;
26936         }
26937     },
26938     
26939     onLoadCanvas : function()
26940     {   
26941         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26942         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26943         
26944         this.bodyEl.un('click', this.beforeSelectFile, this);
26945         
26946         this.notifyEl.hide();
26947         this.thumbEl.show();
26948         this.footerEl.show();
26949         
26950         this.baseRotateLevel();
26951         
26952         if(this.isDocument){
26953             this.setThumbBoxSize();
26954         }
26955         
26956         this.setThumbBoxPosition();
26957         
26958         this.baseScaleLevel();
26959         
26960         this.draw();
26961         
26962         this.resize();
26963         
26964         this.canvasLoaded = true;
26965         
26966         if(this.loadMask){
26967             this.maskEl.unmask();
26968         }
26969         
26970     },
26971     
26972     setCanvasPosition : function()
26973     {   
26974         if(!this.canvasEl){
26975             return;
26976         }
26977         
26978         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26979         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26980         
26981         this.previewEl.setLeft(pw);
26982         this.previewEl.setTop(ph);
26983         
26984     },
26985     
26986     onMouseDown : function(e)
26987     {   
26988         e.stopEvent();
26989         
26990         this.dragable = true;
26991         this.pinching = false;
26992         
26993         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26994             this.dragable = false;
26995             return;
26996         }
26997         
26998         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26999         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27000         
27001     },
27002     
27003     onMouseMove : function(e)
27004     {   
27005         e.stopEvent();
27006         
27007         if(!this.canvasLoaded){
27008             return;
27009         }
27010         
27011         if (!this.dragable){
27012             return;
27013         }
27014         
27015         var minX = Math.ceil(this.thumbEl.getLeft(true));
27016         var minY = Math.ceil(this.thumbEl.getTop(true));
27017         
27018         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27019         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27020         
27021         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27022         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27023         
27024         x = x - this.mouseX;
27025         y = y - this.mouseY;
27026         
27027         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27028         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27029         
27030         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27031         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27032         
27033         this.previewEl.setLeft(bgX);
27034         this.previewEl.setTop(bgY);
27035         
27036         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27037         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27038     },
27039     
27040     onMouseUp : function(e)
27041     {   
27042         e.stopEvent();
27043         
27044         this.dragable = false;
27045     },
27046     
27047     onMouseWheel : function(e)
27048     {   
27049         e.stopEvent();
27050         
27051         this.startScale = this.scale;
27052         
27053         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27054         
27055         if(!this.zoomable()){
27056             this.scale = this.startScale;
27057             return;
27058         }
27059         
27060         this.draw();
27061         
27062         return;
27063     },
27064     
27065     zoomable : function()
27066     {
27067         var minScale = this.thumbEl.getWidth() / this.minWidth;
27068         
27069         if(this.minWidth < this.minHeight){
27070             minScale = this.thumbEl.getHeight() / this.minHeight;
27071         }
27072         
27073         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27074         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27075         
27076         if(
27077                 this.isDocument &&
27078                 (this.rotate == 0 || this.rotate == 180) && 
27079                 (
27080                     width > this.imageEl.OriginWidth || 
27081                     height > this.imageEl.OriginHeight ||
27082                     (width < this.minWidth && height < this.minHeight)
27083                 )
27084         ){
27085             return false;
27086         }
27087         
27088         if(
27089                 this.isDocument &&
27090                 (this.rotate == 90 || this.rotate == 270) && 
27091                 (
27092                     width > this.imageEl.OriginWidth || 
27093                     height > this.imageEl.OriginHeight ||
27094                     (width < this.minHeight && height < this.minWidth)
27095                 )
27096         ){
27097             return false;
27098         }
27099         
27100         if(
27101                 !this.isDocument &&
27102                 (this.rotate == 0 || this.rotate == 180) && 
27103                 (
27104                     width < this.minWidth || 
27105                     width > this.imageEl.OriginWidth || 
27106                     height < this.minHeight || 
27107                     height > this.imageEl.OriginHeight
27108                 )
27109         ){
27110             return false;
27111         }
27112         
27113         if(
27114                 !this.isDocument &&
27115                 (this.rotate == 90 || this.rotate == 270) && 
27116                 (
27117                     width < this.minHeight || 
27118                     width > this.imageEl.OriginWidth || 
27119                     height < this.minWidth || 
27120                     height > this.imageEl.OriginHeight
27121                 )
27122         ){
27123             return false;
27124         }
27125         
27126         return true;
27127         
27128     },
27129     
27130     onRotateLeft : function(e)
27131     {   
27132         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27133             
27134             var minScale = this.thumbEl.getWidth() / this.minWidth;
27135             
27136             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27137             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27138             
27139             this.startScale = this.scale;
27140             
27141             while (this.getScaleLevel() < minScale){
27142             
27143                 this.scale = this.scale + 1;
27144                 
27145                 if(!this.zoomable()){
27146                     break;
27147                 }
27148                 
27149                 if(
27150                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27151                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27152                 ){
27153                     continue;
27154                 }
27155                 
27156                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27157
27158                 this.draw();
27159                 
27160                 return;
27161             }
27162             
27163             this.scale = this.startScale;
27164             
27165             this.onRotateFail();
27166             
27167             return false;
27168         }
27169         
27170         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27171
27172         if(this.isDocument){
27173             this.setThumbBoxSize();
27174             this.setThumbBoxPosition();
27175             this.setCanvasPosition();
27176         }
27177         
27178         this.draw();
27179         
27180         this.fireEvent('rotate', this, 'left');
27181         
27182     },
27183     
27184     onRotateRight : function(e)
27185     {
27186         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27187             
27188             var minScale = this.thumbEl.getWidth() / this.minWidth;
27189         
27190             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27191             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27192             
27193             this.startScale = this.scale;
27194             
27195             while (this.getScaleLevel() < minScale){
27196             
27197                 this.scale = this.scale + 1;
27198                 
27199                 if(!this.zoomable()){
27200                     break;
27201                 }
27202                 
27203                 if(
27204                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27205                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27206                 ){
27207                     continue;
27208                 }
27209                 
27210                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27211
27212                 this.draw();
27213                 
27214                 return;
27215             }
27216             
27217             this.scale = this.startScale;
27218             
27219             this.onRotateFail();
27220             
27221             return false;
27222         }
27223         
27224         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27225
27226         if(this.isDocument){
27227             this.setThumbBoxSize();
27228             this.setThumbBoxPosition();
27229             this.setCanvasPosition();
27230         }
27231         
27232         this.draw();
27233         
27234         this.fireEvent('rotate', this, 'right');
27235     },
27236     
27237     onRotateFail : function()
27238     {
27239         this.errorEl.show(true);
27240         
27241         var _this = this;
27242         
27243         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27244     },
27245     
27246     draw : function()
27247     {
27248         this.previewEl.dom.innerHTML = '';
27249         
27250         var canvasEl = document.createElement("canvas");
27251         
27252         var contextEl = canvasEl.getContext("2d");
27253         
27254         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27255         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27256         var center = this.imageEl.OriginWidth / 2;
27257         
27258         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27259             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27260             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27261             center = this.imageEl.OriginHeight / 2;
27262         }
27263         
27264         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27265         
27266         contextEl.translate(center, center);
27267         contextEl.rotate(this.rotate * Math.PI / 180);
27268
27269         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27270         
27271         this.canvasEl = document.createElement("canvas");
27272         
27273         this.contextEl = this.canvasEl.getContext("2d");
27274         
27275         switch (this.rotate) {
27276             case 0 :
27277                 
27278                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27279                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27280                 
27281                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27282                 
27283                 break;
27284             case 90 : 
27285                 
27286                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27287                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27288                 
27289                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27290                     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);
27291                     break;
27292                 }
27293                 
27294                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27295                 
27296                 break;
27297             case 180 :
27298                 
27299                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27300                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27301                 
27302                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27303                     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);
27304                     break;
27305                 }
27306                 
27307                 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);
27308                 
27309                 break;
27310             case 270 :
27311                 
27312                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27313                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27314         
27315                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27316                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27317                     break;
27318                 }
27319                 
27320                 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);
27321                 
27322                 break;
27323             default : 
27324                 break;
27325         }
27326         
27327         this.previewEl.appendChild(this.canvasEl);
27328         
27329         this.setCanvasPosition();
27330     },
27331     
27332     crop : function()
27333     {
27334         if(!this.canvasLoaded){
27335             return;
27336         }
27337         
27338         var imageCanvas = document.createElement("canvas");
27339         
27340         var imageContext = imageCanvas.getContext("2d");
27341         
27342         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27343         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27344         
27345         var center = imageCanvas.width / 2;
27346         
27347         imageContext.translate(center, center);
27348         
27349         imageContext.rotate(this.rotate * Math.PI / 180);
27350         
27351         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27352         
27353         var canvas = document.createElement("canvas");
27354         
27355         var context = canvas.getContext("2d");
27356                 
27357         canvas.width = this.minWidth;
27358         canvas.height = this.minHeight;
27359
27360         switch (this.rotate) {
27361             case 0 :
27362                 
27363                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27364                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27365                 
27366                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27367                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27368                 
27369                 var targetWidth = this.minWidth - 2 * x;
27370                 var targetHeight = this.minHeight - 2 * y;
27371                 
27372                 var scale = 1;
27373                 
27374                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27375                     scale = targetWidth / width;
27376                 }
27377                 
27378                 if(x > 0 && y == 0){
27379                     scale = targetHeight / height;
27380                 }
27381                 
27382                 if(x > 0 && y > 0){
27383                     scale = targetWidth / width;
27384                     
27385                     if(width < height){
27386                         scale = targetHeight / height;
27387                     }
27388                 }
27389                 
27390                 context.scale(scale, scale);
27391                 
27392                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27393                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27394
27395                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27396                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27397
27398                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27399                 
27400                 break;
27401             case 90 : 
27402                 
27403                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27404                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27405                 
27406                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27407                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27408                 
27409                 var targetWidth = this.minWidth - 2 * x;
27410                 var targetHeight = this.minHeight - 2 * y;
27411                 
27412                 var scale = 1;
27413                 
27414                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27415                     scale = targetWidth / width;
27416                 }
27417                 
27418                 if(x > 0 && y == 0){
27419                     scale = targetHeight / height;
27420                 }
27421                 
27422                 if(x > 0 && y > 0){
27423                     scale = targetWidth / width;
27424                     
27425                     if(width < height){
27426                         scale = targetHeight / height;
27427                     }
27428                 }
27429                 
27430                 context.scale(scale, scale);
27431                 
27432                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27433                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27434
27435                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27436                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27437                 
27438                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27439                 
27440                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27441                 
27442                 break;
27443             case 180 :
27444                 
27445                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27446                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27447                 
27448                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27449                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27450                 
27451                 var targetWidth = this.minWidth - 2 * x;
27452                 var targetHeight = this.minHeight - 2 * y;
27453                 
27454                 var scale = 1;
27455                 
27456                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27457                     scale = targetWidth / width;
27458                 }
27459                 
27460                 if(x > 0 && y == 0){
27461                     scale = targetHeight / height;
27462                 }
27463                 
27464                 if(x > 0 && y > 0){
27465                     scale = targetWidth / width;
27466                     
27467                     if(width < height){
27468                         scale = targetHeight / height;
27469                     }
27470                 }
27471                 
27472                 context.scale(scale, scale);
27473                 
27474                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27475                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27476
27477                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27478                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27479
27480                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27481                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27482                 
27483                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27484                 
27485                 break;
27486             case 270 :
27487                 
27488                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27489                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27490                 
27491                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27492                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27493                 
27494                 var targetWidth = this.minWidth - 2 * x;
27495                 var targetHeight = this.minHeight - 2 * y;
27496                 
27497                 var scale = 1;
27498                 
27499                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27500                     scale = targetWidth / width;
27501                 }
27502                 
27503                 if(x > 0 && y == 0){
27504                     scale = targetHeight / height;
27505                 }
27506                 
27507                 if(x > 0 && y > 0){
27508                     scale = targetWidth / width;
27509                     
27510                     if(width < height){
27511                         scale = targetHeight / height;
27512                     }
27513                 }
27514                 
27515                 context.scale(scale, scale);
27516                 
27517                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27518                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27519
27520                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27521                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27522                 
27523                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27524                 
27525                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27526                 
27527                 break;
27528             default : 
27529                 break;
27530         }
27531         
27532         this.cropData = canvas.toDataURL(this.cropType);
27533         
27534         if(this.fireEvent('crop', this, this.cropData) !== false){
27535             this.process(this.file, this.cropData);
27536         }
27537         
27538         return;
27539         
27540     },
27541     
27542     setThumbBoxSize : function()
27543     {
27544         var width, height;
27545         
27546         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27547             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27548             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27549             
27550             this.minWidth = width;
27551             this.minHeight = height;
27552             
27553             if(this.rotate == 90 || this.rotate == 270){
27554                 this.minWidth = height;
27555                 this.minHeight = width;
27556             }
27557         }
27558         
27559         height = 300;
27560         width = Math.ceil(this.minWidth * height / this.minHeight);
27561         
27562         if(this.minWidth > this.minHeight){
27563             width = 300;
27564             height = Math.ceil(this.minHeight * width / this.minWidth);
27565         }
27566         
27567         this.thumbEl.setStyle({
27568             width : width + 'px',
27569             height : height + 'px'
27570         });
27571
27572         return;
27573             
27574     },
27575     
27576     setThumbBoxPosition : function()
27577     {
27578         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27579         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27580         
27581         this.thumbEl.setLeft(x);
27582         this.thumbEl.setTop(y);
27583         
27584     },
27585     
27586     baseRotateLevel : function()
27587     {
27588         this.baseRotate = 1;
27589         
27590         if(
27591                 typeof(this.exif) != 'undefined' &&
27592                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27593                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27594         ){
27595             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27596         }
27597         
27598         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27599         
27600     },
27601     
27602     baseScaleLevel : function()
27603     {
27604         var width, height;
27605         
27606         if(this.isDocument){
27607             
27608             if(this.baseRotate == 6 || this.baseRotate == 8){
27609             
27610                 height = this.thumbEl.getHeight();
27611                 this.baseScale = height / this.imageEl.OriginWidth;
27612
27613                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27614                     width = this.thumbEl.getWidth();
27615                     this.baseScale = width / this.imageEl.OriginHeight;
27616                 }
27617
27618                 return;
27619             }
27620
27621             height = this.thumbEl.getHeight();
27622             this.baseScale = height / this.imageEl.OriginHeight;
27623
27624             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27625                 width = this.thumbEl.getWidth();
27626                 this.baseScale = width / this.imageEl.OriginWidth;
27627             }
27628
27629             return;
27630         }
27631         
27632         if(this.baseRotate == 6 || this.baseRotate == 8){
27633             
27634             width = this.thumbEl.getHeight();
27635             this.baseScale = width / this.imageEl.OriginHeight;
27636             
27637             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27638                 height = this.thumbEl.getWidth();
27639                 this.baseScale = height / this.imageEl.OriginHeight;
27640             }
27641             
27642             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27643                 height = this.thumbEl.getWidth();
27644                 this.baseScale = height / this.imageEl.OriginHeight;
27645                 
27646                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27647                     width = this.thumbEl.getHeight();
27648                     this.baseScale = width / this.imageEl.OriginWidth;
27649                 }
27650             }
27651             
27652             return;
27653         }
27654         
27655         width = this.thumbEl.getWidth();
27656         this.baseScale = width / this.imageEl.OriginWidth;
27657         
27658         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27659             height = this.thumbEl.getHeight();
27660             this.baseScale = height / this.imageEl.OriginHeight;
27661         }
27662         
27663         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27664             
27665             height = this.thumbEl.getHeight();
27666             this.baseScale = height / this.imageEl.OriginHeight;
27667             
27668             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27669                 width = this.thumbEl.getWidth();
27670                 this.baseScale = width / this.imageEl.OriginWidth;
27671             }
27672             
27673         }
27674         
27675         return;
27676     },
27677     
27678     getScaleLevel : function()
27679     {
27680         return this.baseScale * Math.pow(1.1, this.scale);
27681     },
27682     
27683     onTouchStart : function(e)
27684     {
27685         if(!this.canvasLoaded){
27686             this.beforeSelectFile(e);
27687             return;
27688         }
27689         
27690         var touches = e.browserEvent.touches;
27691         
27692         if(!touches){
27693             return;
27694         }
27695         
27696         if(touches.length == 1){
27697             this.onMouseDown(e);
27698             return;
27699         }
27700         
27701         if(touches.length != 2){
27702             return;
27703         }
27704         
27705         var coords = [];
27706         
27707         for(var i = 0, finger; finger = touches[i]; i++){
27708             coords.push(finger.pageX, finger.pageY);
27709         }
27710         
27711         var x = Math.pow(coords[0] - coords[2], 2);
27712         var y = Math.pow(coords[1] - coords[3], 2);
27713         
27714         this.startDistance = Math.sqrt(x + y);
27715         
27716         this.startScale = this.scale;
27717         
27718         this.pinching = true;
27719         this.dragable = false;
27720         
27721     },
27722     
27723     onTouchMove : function(e)
27724     {
27725         if(!this.pinching && !this.dragable){
27726             return;
27727         }
27728         
27729         var touches = e.browserEvent.touches;
27730         
27731         if(!touches){
27732             return;
27733         }
27734         
27735         if(this.dragable){
27736             this.onMouseMove(e);
27737             return;
27738         }
27739         
27740         var coords = [];
27741         
27742         for(var i = 0, finger; finger = touches[i]; i++){
27743             coords.push(finger.pageX, finger.pageY);
27744         }
27745         
27746         var x = Math.pow(coords[0] - coords[2], 2);
27747         var y = Math.pow(coords[1] - coords[3], 2);
27748         
27749         this.endDistance = Math.sqrt(x + y);
27750         
27751         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27752         
27753         if(!this.zoomable()){
27754             this.scale = this.startScale;
27755             return;
27756         }
27757         
27758         this.draw();
27759         
27760     },
27761     
27762     onTouchEnd : function(e)
27763     {
27764         this.pinching = false;
27765         this.dragable = false;
27766         
27767     },
27768     
27769     process : function(file, crop)
27770     {
27771         if(this.loadMask){
27772             this.maskEl.mask(this.loadingText);
27773         }
27774         
27775         this.xhr = new XMLHttpRequest();
27776         
27777         file.xhr = this.xhr;
27778
27779         this.xhr.open(this.method, this.url, true);
27780         
27781         var headers = {
27782             "Accept": "application/json",
27783             "Cache-Control": "no-cache",
27784             "X-Requested-With": "XMLHttpRequest"
27785         };
27786         
27787         for (var headerName in headers) {
27788             var headerValue = headers[headerName];
27789             if (headerValue) {
27790                 this.xhr.setRequestHeader(headerName, headerValue);
27791             }
27792         }
27793         
27794         var _this = this;
27795         
27796         this.xhr.onload = function()
27797         {
27798             _this.xhrOnLoad(_this.xhr);
27799         }
27800         
27801         this.xhr.onerror = function()
27802         {
27803             _this.xhrOnError(_this.xhr);
27804         }
27805         
27806         var formData = new FormData();
27807
27808         formData.append('returnHTML', 'NO');
27809         
27810         if(crop){
27811             formData.append('crop', crop);
27812         }
27813         
27814         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27815             formData.append(this.paramName, file, file.name);
27816         }
27817         
27818         if(typeof(file.filename) != 'undefined'){
27819             formData.append('filename', file.filename);
27820         }
27821         
27822         if(typeof(file.mimetype) != 'undefined'){
27823             formData.append('mimetype', file.mimetype);
27824         }
27825         
27826         if(this.fireEvent('arrange', this, formData) != false){
27827             this.xhr.send(formData);
27828         };
27829     },
27830     
27831     xhrOnLoad : function(xhr)
27832     {
27833         if(this.loadMask){
27834             this.maskEl.unmask();
27835         }
27836         
27837         if (xhr.readyState !== 4) {
27838             this.fireEvent('exception', this, xhr);
27839             return;
27840         }
27841
27842         var response = Roo.decode(xhr.responseText);
27843         
27844         if(!response.success){
27845             this.fireEvent('exception', this, xhr);
27846             return;
27847         }
27848         
27849         var response = Roo.decode(xhr.responseText);
27850         
27851         this.fireEvent('upload', this, response);
27852         
27853     },
27854     
27855     xhrOnError : function()
27856     {
27857         if(this.loadMask){
27858             this.maskEl.unmask();
27859         }
27860         
27861         Roo.log('xhr on error');
27862         
27863         var response = Roo.decode(xhr.responseText);
27864           
27865         Roo.log(response);
27866         
27867     },
27868     
27869     prepare : function(file)
27870     {   
27871         if(this.loadMask){
27872             this.maskEl.mask(this.loadingText);
27873         }
27874         
27875         this.file = false;
27876         this.exif = {};
27877         
27878         if(typeof(file) === 'string'){
27879             this.loadCanvas(file);
27880             return;
27881         }
27882         
27883         if(!file || !this.urlAPI){
27884             return;
27885         }
27886         
27887         this.file = file;
27888         this.cropType = file.type;
27889         
27890         var _this = this;
27891         
27892         if(this.fireEvent('prepare', this, this.file) != false){
27893             
27894             var reader = new FileReader();
27895             
27896             reader.onload = function (e) {
27897                 if (e.target.error) {
27898                     Roo.log(e.target.error);
27899                     return;
27900                 }
27901                 
27902                 var buffer = e.target.result,
27903                     dataView = new DataView(buffer),
27904                     offset = 2,
27905                     maxOffset = dataView.byteLength - 4,
27906                     markerBytes,
27907                     markerLength;
27908                 
27909                 if (dataView.getUint16(0) === 0xffd8) {
27910                     while (offset < maxOffset) {
27911                         markerBytes = dataView.getUint16(offset);
27912                         
27913                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27914                             markerLength = dataView.getUint16(offset + 2) + 2;
27915                             if (offset + markerLength > dataView.byteLength) {
27916                                 Roo.log('Invalid meta data: Invalid segment size.');
27917                                 break;
27918                             }
27919                             
27920                             if(markerBytes == 0xffe1){
27921                                 _this.parseExifData(
27922                                     dataView,
27923                                     offset,
27924                                     markerLength
27925                                 );
27926                             }
27927                             
27928                             offset += markerLength;
27929                             
27930                             continue;
27931                         }
27932                         
27933                         break;
27934                     }
27935                     
27936                 }
27937                 
27938                 var url = _this.urlAPI.createObjectURL(_this.file);
27939                 
27940                 _this.loadCanvas(url);
27941                 
27942                 return;
27943             }
27944             
27945             reader.readAsArrayBuffer(this.file);
27946             
27947         }
27948         
27949     },
27950     
27951     parseExifData : function(dataView, offset, length)
27952     {
27953         var tiffOffset = offset + 10,
27954             littleEndian,
27955             dirOffset;
27956     
27957         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27958             // No Exif data, might be XMP data instead
27959             return;
27960         }
27961         
27962         // Check for the ASCII code for "Exif" (0x45786966):
27963         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27964             // No Exif data, might be XMP data instead
27965             return;
27966         }
27967         if (tiffOffset + 8 > dataView.byteLength) {
27968             Roo.log('Invalid Exif data: Invalid segment size.');
27969             return;
27970         }
27971         // Check for the two null bytes:
27972         if (dataView.getUint16(offset + 8) !== 0x0000) {
27973             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27974             return;
27975         }
27976         // Check the byte alignment:
27977         switch (dataView.getUint16(tiffOffset)) {
27978         case 0x4949:
27979             littleEndian = true;
27980             break;
27981         case 0x4D4D:
27982             littleEndian = false;
27983             break;
27984         default:
27985             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27986             return;
27987         }
27988         // Check for the TIFF tag marker (0x002A):
27989         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27990             Roo.log('Invalid Exif data: Missing TIFF marker.');
27991             return;
27992         }
27993         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27994         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27995         
27996         this.parseExifTags(
27997             dataView,
27998             tiffOffset,
27999             tiffOffset + dirOffset,
28000             littleEndian
28001         );
28002     },
28003     
28004     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28005     {
28006         var tagsNumber,
28007             dirEndOffset,
28008             i;
28009         if (dirOffset + 6 > dataView.byteLength) {
28010             Roo.log('Invalid Exif data: Invalid directory offset.');
28011             return;
28012         }
28013         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28014         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28015         if (dirEndOffset + 4 > dataView.byteLength) {
28016             Roo.log('Invalid Exif data: Invalid directory size.');
28017             return;
28018         }
28019         for (i = 0; i < tagsNumber; i += 1) {
28020             this.parseExifTag(
28021                 dataView,
28022                 tiffOffset,
28023                 dirOffset + 2 + 12 * i, // tag offset
28024                 littleEndian
28025             );
28026         }
28027         // Return the offset to the next directory:
28028         return dataView.getUint32(dirEndOffset, littleEndian);
28029     },
28030     
28031     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28032     {
28033         var tag = dataView.getUint16(offset, littleEndian);
28034         
28035         this.exif[tag] = this.getExifValue(
28036             dataView,
28037             tiffOffset,
28038             offset,
28039             dataView.getUint16(offset + 2, littleEndian), // tag type
28040             dataView.getUint32(offset + 4, littleEndian), // tag length
28041             littleEndian
28042         );
28043     },
28044     
28045     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28046     {
28047         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28048             tagSize,
28049             dataOffset,
28050             values,
28051             i,
28052             str,
28053             c;
28054     
28055         if (!tagType) {
28056             Roo.log('Invalid Exif data: Invalid tag type.');
28057             return;
28058         }
28059         
28060         tagSize = tagType.size * length;
28061         // Determine if the value is contained in the dataOffset bytes,
28062         // or if the value at the dataOffset is a pointer to the actual data:
28063         dataOffset = tagSize > 4 ?
28064                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28065         if (dataOffset + tagSize > dataView.byteLength) {
28066             Roo.log('Invalid Exif data: Invalid data offset.');
28067             return;
28068         }
28069         if (length === 1) {
28070             return tagType.getValue(dataView, dataOffset, littleEndian);
28071         }
28072         values = [];
28073         for (i = 0; i < length; i += 1) {
28074             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28075         }
28076         
28077         if (tagType.ascii) {
28078             str = '';
28079             // Concatenate the chars:
28080             for (i = 0; i < values.length; i += 1) {
28081                 c = values[i];
28082                 // Ignore the terminating NULL byte(s):
28083                 if (c === '\u0000') {
28084                     break;
28085                 }
28086                 str += c;
28087             }
28088             return str;
28089         }
28090         return values;
28091     }
28092     
28093 });
28094
28095 Roo.apply(Roo.bootstrap.UploadCropbox, {
28096     tags : {
28097         'Orientation': 0x0112
28098     },
28099     
28100     Orientation: {
28101             1: 0, //'top-left',
28102 //            2: 'top-right',
28103             3: 180, //'bottom-right',
28104 //            4: 'bottom-left',
28105 //            5: 'left-top',
28106             6: 90, //'right-top',
28107 //            7: 'right-bottom',
28108             8: 270 //'left-bottom'
28109     },
28110     
28111     exifTagTypes : {
28112         // byte, 8-bit unsigned int:
28113         1: {
28114             getValue: function (dataView, dataOffset) {
28115                 return dataView.getUint8(dataOffset);
28116             },
28117             size: 1
28118         },
28119         // ascii, 8-bit byte:
28120         2: {
28121             getValue: function (dataView, dataOffset) {
28122                 return String.fromCharCode(dataView.getUint8(dataOffset));
28123             },
28124             size: 1,
28125             ascii: true
28126         },
28127         // short, 16 bit int:
28128         3: {
28129             getValue: function (dataView, dataOffset, littleEndian) {
28130                 return dataView.getUint16(dataOffset, littleEndian);
28131             },
28132             size: 2
28133         },
28134         // long, 32 bit int:
28135         4: {
28136             getValue: function (dataView, dataOffset, littleEndian) {
28137                 return dataView.getUint32(dataOffset, littleEndian);
28138             },
28139             size: 4
28140         },
28141         // rational = two long values, first is numerator, second is denominator:
28142         5: {
28143             getValue: function (dataView, dataOffset, littleEndian) {
28144                 return dataView.getUint32(dataOffset, littleEndian) /
28145                     dataView.getUint32(dataOffset + 4, littleEndian);
28146             },
28147             size: 8
28148         },
28149         // slong, 32 bit signed int:
28150         9: {
28151             getValue: function (dataView, dataOffset, littleEndian) {
28152                 return dataView.getInt32(dataOffset, littleEndian);
28153             },
28154             size: 4
28155         },
28156         // srational, two slongs, first is numerator, second is denominator:
28157         10: {
28158             getValue: function (dataView, dataOffset, littleEndian) {
28159                 return dataView.getInt32(dataOffset, littleEndian) /
28160                     dataView.getInt32(dataOffset + 4, littleEndian);
28161             },
28162             size: 8
28163         }
28164     },
28165     
28166     footer : {
28167         STANDARD : [
28168             {
28169                 tag : 'div',
28170                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28171                 action : 'rotate-left',
28172                 cn : [
28173                     {
28174                         tag : 'button',
28175                         cls : 'btn btn-default',
28176                         html : '<i class="fa fa-undo"></i>'
28177                     }
28178                 ]
28179             },
28180             {
28181                 tag : 'div',
28182                 cls : 'btn-group roo-upload-cropbox-picture',
28183                 action : 'picture',
28184                 cn : [
28185                     {
28186                         tag : 'button',
28187                         cls : 'btn btn-default',
28188                         html : '<i class="fa fa-picture-o"></i>'
28189                     }
28190                 ]
28191             },
28192             {
28193                 tag : 'div',
28194                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28195                 action : 'rotate-right',
28196                 cn : [
28197                     {
28198                         tag : 'button',
28199                         cls : 'btn btn-default',
28200                         html : '<i class="fa fa-repeat"></i>'
28201                     }
28202                 ]
28203             }
28204         ],
28205         DOCUMENT : [
28206             {
28207                 tag : 'div',
28208                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28209                 action : 'rotate-left',
28210                 cn : [
28211                     {
28212                         tag : 'button',
28213                         cls : 'btn btn-default',
28214                         html : '<i class="fa fa-undo"></i>'
28215                     }
28216                 ]
28217             },
28218             {
28219                 tag : 'div',
28220                 cls : 'btn-group roo-upload-cropbox-download',
28221                 action : 'download',
28222                 cn : [
28223                     {
28224                         tag : 'button',
28225                         cls : 'btn btn-default',
28226                         html : '<i class="fa fa-download"></i>'
28227                     }
28228                 ]
28229             },
28230             {
28231                 tag : 'div',
28232                 cls : 'btn-group roo-upload-cropbox-crop',
28233                 action : 'crop',
28234                 cn : [
28235                     {
28236                         tag : 'button',
28237                         cls : 'btn btn-default',
28238                         html : '<i class="fa fa-crop"></i>'
28239                     }
28240                 ]
28241             },
28242             {
28243                 tag : 'div',
28244                 cls : 'btn-group roo-upload-cropbox-trash',
28245                 action : 'trash',
28246                 cn : [
28247                     {
28248                         tag : 'button',
28249                         cls : 'btn btn-default',
28250                         html : '<i class="fa fa-trash"></i>'
28251                     }
28252                 ]
28253             },
28254             {
28255                 tag : 'div',
28256                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28257                 action : 'rotate-right',
28258                 cn : [
28259                     {
28260                         tag : 'button',
28261                         cls : 'btn btn-default',
28262                         html : '<i class="fa fa-repeat"></i>'
28263                     }
28264                 ]
28265             }
28266         ],
28267         ROTATOR : [
28268             {
28269                 tag : 'div',
28270                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28271                 action : 'rotate-left',
28272                 cn : [
28273                     {
28274                         tag : 'button',
28275                         cls : 'btn btn-default',
28276                         html : '<i class="fa fa-undo"></i>'
28277                     }
28278                 ]
28279             },
28280             {
28281                 tag : 'div',
28282                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28283                 action : 'rotate-right',
28284                 cn : [
28285                     {
28286                         tag : 'button',
28287                         cls : 'btn btn-default',
28288                         html : '<i class="fa fa-repeat"></i>'
28289                     }
28290                 ]
28291             }
28292         ]
28293     }
28294 });
28295
28296 /*
28297 * Licence: LGPL
28298 */
28299
28300 /**
28301  * @class Roo.bootstrap.DocumentManager
28302  * @extends Roo.bootstrap.Component
28303  * Bootstrap DocumentManager class
28304  * @cfg {String} paramName default 'imageUpload'
28305  * @cfg {String} toolTipName default 'filename'
28306  * @cfg {String} method default POST
28307  * @cfg {String} url action url
28308  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28309  * @cfg {Boolean} multiple multiple upload default true
28310  * @cfg {Number} thumbSize default 300
28311  * @cfg {String} fieldLabel
28312  * @cfg {Number} labelWidth default 4
28313  * @cfg {String} labelAlign (left|top) default left
28314  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28315 * @cfg {Number} labellg set the width of label (1-12)
28316  * @cfg {Number} labelmd set the width of label (1-12)
28317  * @cfg {Number} labelsm set the width of label (1-12)
28318  * @cfg {Number} labelxs set the width of label (1-12)
28319  * 
28320  * @constructor
28321  * Create a new DocumentManager
28322  * @param {Object} config The config object
28323  */
28324
28325 Roo.bootstrap.DocumentManager = function(config){
28326     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28327     
28328     this.files = [];
28329     this.delegates = [];
28330     
28331     this.addEvents({
28332         /**
28333          * @event initial
28334          * Fire when initial the DocumentManager
28335          * @param {Roo.bootstrap.DocumentManager} this
28336          */
28337         "initial" : true,
28338         /**
28339          * @event inspect
28340          * inspect selected file
28341          * @param {Roo.bootstrap.DocumentManager} this
28342          * @param {File} file
28343          */
28344         "inspect" : true,
28345         /**
28346          * @event exception
28347          * Fire when xhr load exception
28348          * @param {Roo.bootstrap.DocumentManager} this
28349          * @param {XMLHttpRequest} xhr
28350          */
28351         "exception" : true,
28352         /**
28353          * @event afterupload
28354          * Fire when xhr load exception
28355          * @param {Roo.bootstrap.DocumentManager} this
28356          * @param {XMLHttpRequest} xhr
28357          */
28358         "afterupload" : true,
28359         /**
28360          * @event prepare
28361          * prepare the form data
28362          * @param {Roo.bootstrap.DocumentManager} this
28363          * @param {Object} formData
28364          */
28365         "prepare" : true,
28366         /**
28367          * @event remove
28368          * Fire when remove the file
28369          * @param {Roo.bootstrap.DocumentManager} this
28370          * @param {Object} file
28371          */
28372         "remove" : true,
28373         /**
28374          * @event refresh
28375          * Fire after refresh the file
28376          * @param {Roo.bootstrap.DocumentManager} this
28377          */
28378         "refresh" : true,
28379         /**
28380          * @event click
28381          * Fire after click the image
28382          * @param {Roo.bootstrap.DocumentManager} this
28383          * @param {Object} file
28384          */
28385         "click" : true,
28386         /**
28387          * @event edit
28388          * Fire when upload a image and editable set to true
28389          * @param {Roo.bootstrap.DocumentManager} this
28390          * @param {Object} file
28391          */
28392         "edit" : true,
28393         /**
28394          * @event beforeselectfile
28395          * Fire before select file
28396          * @param {Roo.bootstrap.DocumentManager} this
28397          */
28398         "beforeselectfile" : true,
28399         /**
28400          * @event process
28401          * Fire before process file
28402          * @param {Roo.bootstrap.DocumentManager} this
28403          * @param {Object} file
28404          */
28405         "process" : true
28406         
28407     });
28408 };
28409
28410 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28411     
28412     boxes : 0,
28413     inputName : '',
28414     thumbSize : 300,
28415     multiple : true,
28416     files : false,
28417     method : 'POST',
28418     url : '',
28419     paramName : 'imageUpload',
28420     toolTipName : 'filename',
28421     fieldLabel : '',
28422     labelWidth : 4,
28423     labelAlign : 'left',
28424     editable : true,
28425     delegates : false,
28426     xhr : false, 
28427     
28428     labellg : 0,
28429     labelmd : 0,
28430     labelsm : 0,
28431     labelxs : 0,
28432     
28433     getAutoCreate : function()
28434     {   
28435         var managerWidget = {
28436             tag : 'div',
28437             cls : 'roo-document-manager',
28438             cn : [
28439                 {
28440                     tag : 'input',
28441                     cls : 'roo-document-manager-selector',
28442                     type : 'file'
28443                 },
28444                 {
28445                     tag : 'div',
28446                     cls : 'roo-document-manager-uploader',
28447                     cn : [
28448                         {
28449                             tag : 'div',
28450                             cls : 'roo-document-manager-upload-btn',
28451                             html : '<i class="fa fa-plus"></i>'
28452                         }
28453                     ]
28454                     
28455                 }
28456             ]
28457         };
28458         
28459         var content = [
28460             {
28461                 tag : 'div',
28462                 cls : 'column col-md-12',
28463                 cn : managerWidget
28464             }
28465         ];
28466         
28467         if(this.fieldLabel.length){
28468             
28469             content = [
28470                 {
28471                     tag : 'div',
28472                     cls : 'column col-md-12',
28473                     html : this.fieldLabel
28474                 },
28475                 {
28476                     tag : 'div',
28477                     cls : 'column col-md-12',
28478                     cn : managerWidget
28479                 }
28480             ];
28481
28482             if(this.labelAlign == 'left'){
28483                 content = [
28484                     {
28485                         tag : 'div',
28486                         cls : 'column',
28487                         html : this.fieldLabel
28488                     },
28489                     {
28490                         tag : 'div',
28491                         cls : 'column',
28492                         cn : managerWidget
28493                     }
28494                 ];
28495                 
28496                 if(this.labelWidth > 12){
28497                     content[0].style = "width: " + this.labelWidth + 'px';
28498                 }
28499
28500                 if(this.labelWidth < 13 && this.labelmd == 0){
28501                     this.labelmd = this.labelWidth;
28502                 }
28503
28504                 if(this.labellg > 0){
28505                     content[0].cls += ' col-lg-' + this.labellg;
28506                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28507                 }
28508
28509                 if(this.labelmd > 0){
28510                     content[0].cls += ' col-md-' + this.labelmd;
28511                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28512                 }
28513
28514                 if(this.labelsm > 0){
28515                     content[0].cls += ' col-sm-' + this.labelsm;
28516                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28517                 }
28518
28519                 if(this.labelxs > 0){
28520                     content[0].cls += ' col-xs-' + this.labelxs;
28521                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28522                 }
28523                 
28524             }
28525         }
28526         
28527         var cfg = {
28528             tag : 'div',
28529             cls : 'row clearfix',
28530             cn : content
28531         };
28532         
28533         return cfg;
28534         
28535     },
28536     
28537     initEvents : function()
28538     {
28539         this.managerEl = this.el.select('.roo-document-manager', true).first();
28540         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28541         
28542         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28543         this.selectorEl.hide();
28544         
28545         if(this.multiple){
28546             this.selectorEl.attr('multiple', 'multiple');
28547         }
28548         
28549         this.selectorEl.on('change', this.onFileSelected, this);
28550         
28551         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28552         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28553         
28554         this.uploader.on('click', this.onUploaderClick, this);
28555         
28556         this.renderProgressDialog();
28557         
28558         var _this = this;
28559         
28560         window.addEventListener("resize", function() { _this.refresh(); } );
28561         
28562         this.fireEvent('initial', this);
28563     },
28564     
28565     renderProgressDialog : function()
28566     {
28567         var _this = this;
28568         
28569         this.progressDialog = new Roo.bootstrap.Modal({
28570             cls : 'roo-document-manager-progress-dialog',
28571             allow_close : false,
28572             title : '',
28573             buttons : [
28574                 {
28575                     name  :'cancel',
28576                     weight : 'danger',
28577                     html : 'Cancel'
28578                 }
28579             ], 
28580             listeners : { 
28581                 btnclick : function() {
28582                     _this.uploadCancel();
28583                     this.hide();
28584                 }
28585             }
28586         });
28587          
28588         this.progressDialog.render(Roo.get(document.body));
28589          
28590         this.progress = new Roo.bootstrap.Progress({
28591             cls : 'roo-document-manager-progress',
28592             active : true,
28593             striped : true
28594         });
28595         
28596         this.progress.render(this.progressDialog.getChildContainer());
28597         
28598         this.progressBar = new Roo.bootstrap.ProgressBar({
28599             cls : 'roo-document-manager-progress-bar',
28600             aria_valuenow : 0,
28601             aria_valuemin : 0,
28602             aria_valuemax : 12,
28603             panel : 'success'
28604         });
28605         
28606         this.progressBar.render(this.progress.getChildContainer());
28607     },
28608     
28609     onUploaderClick : function(e)
28610     {
28611         e.preventDefault();
28612      
28613         if(this.fireEvent('beforeselectfile', this) != false){
28614             this.selectorEl.dom.click();
28615         }
28616         
28617     },
28618     
28619     onFileSelected : function(e)
28620     {
28621         e.preventDefault();
28622         
28623         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28624             return;
28625         }
28626         
28627         Roo.each(this.selectorEl.dom.files, function(file){
28628             if(this.fireEvent('inspect', this, file) != false){
28629                 this.files.push(file);
28630             }
28631         }, this);
28632         
28633         this.queue();
28634         
28635     },
28636     
28637     queue : function()
28638     {
28639         this.selectorEl.dom.value = '';
28640         
28641         if(!this.files.length){
28642             return;
28643         }
28644         
28645         if(this.boxes > 0 && this.files.length > this.boxes){
28646             this.files = this.files.slice(0, this.boxes);
28647         }
28648         
28649         this.uploader.show();
28650         
28651         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28652             this.uploader.hide();
28653         }
28654         
28655         var _this = this;
28656         
28657         var files = [];
28658         
28659         var docs = [];
28660         
28661         Roo.each(this.files, function(file){
28662             
28663             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28664                 var f = this.renderPreview(file);
28665                 files.push(f);
28666                 return;
28667             }
28668             
28669             if(file.type.indexOf('image') != -1){
28670                 this.delegates.push(
28671                     (function(){
28672                         _this.process(file);
28673                     }).createDelegate(this)
28674                 );
28675         
28676                 return;
28677             }
28678             
28679             docs.push(
28680                 (function(){
28681                     _this.process(file);
28682                 }).createDelegate(this)
28683             );
28684             
28685         }, this);
28686         
28687         this.files = files;
28688         
28689         this.delegates = this.delegates.concat(docs);
28690         
28691         if(!this.delegates.length){
28692             this.refresh();
28693             return;
28694         }
28695         
28696         this.progressBar.aria_valuemax = this.delegates.length;
28697         
28698         this.arrange();
28699         
28700         return;
28701     },
28702     
28703     arrange : function()
28704     {
28705         if(!this.delegates.length){
28706             this.progressDialog.hide();
28707             this.refresh();
28708             return;
28709         }
28710         
28711         var delegate = this.delegates.shift();
28712         
28713         this.progressDialog.show();
28714         
28715         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28716         
28717         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28718         
28719         delegate();
28720     },
28721     
28722     refresh : function()
28723     {
28724         this.uploader.show();
28725         
28726         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28727             this.uploader.hide();
28728         }
28729         
28730         Roo.isTouch ? this.closable(false) : this.closable(true);
28731         
28732         this.fireEvent('refresh', this);
28733     },
28734     
28735     onRemove : function(e, el, o)
28736     {
28737         e.preventDefault();
28738         
28739         this.fireEvent('remove', this, o);
28740         
28741     },
28742     
28743     remove : function(o)
28744     {
28745         var files = [];
28746         
28747         Roo.each(this.files, function(file){
28748             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28749                 files.push(file);
28750                 return;
28751             }
28752
28753             o.target.remove();
28754
28755         }, this);
28756         
28757         this.files = files;
28758         
28759         this.refresh();
28760     },
28761     
28762     clear : function()
28763     {
28764         Roo.each(this.files, function(file){
28765             if(!file.target){
28766                 return;
28767             }
28768             
28769             file.target.remove();
28770
28771         }, this);
28772         
28773         this.files = [];
28774         
28775         this.refresh();
28776     },
28777     
28778     onClick : function(e, el, o)
28779     {
28780         e.preventDefault();
28781         
28782         this.fireEvent('click', this, o);
28783         
28784     },
28785     
28786     closable : function(closable)
28787     {
28788         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28789             
28790             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28791             
28792             if(closable){
28793                 el.show();
28794                 return;
28795             }
28796             
28797             el.hide();
28798             
28799         }, this);
28800     },
28801     
28802     xhrOnLoad : function(xhr)
28803     {
28804         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28805             el.remove();
28806         }, this);
28807         
28808         if (xhr.readyState !== 4) {
28809             this.arrange();
28810             this.fireEvent('exception', this, xhr);
28811             return;
28812         }
28813
28814         var response = Roo.decode(xhr.responseText);
28815         
28816         if(!response.success){
28817             this.arrange();
28818             this.fireEvent('exception', this, xhr);
28819             return;
28820         }
28821         
28822         var file = this.renderPreview(response.data);
28823         
28824         this.files.push(file);
28825         
28826         this.arrange();
28827         
28828         this.fireEvent('afterupload', this, xhr);
28829         
28830     },
28831     
28832     xhrOnError : function(xhr)
28833     {
28834         Roo.log('xhr on error');
28835         
28836         var response = Roo.decode(xhr.responseText);
28837           
28838         Roo.log(response);
28839         
28840         this.arrange();
28841     },
28842     
28843     process : function(file)
28844     {
28845         if(this.fireEvent('process', this, file) !== false){
28846             if(this.editable && file.type.indexOf('image') != -1){
28847                 this.fireEvent('edit', this, file);
28848                 return;
28849             }
28850
28851             this.uploadStart(file, false);
28852
28853             return;
28854         }
28855         
28856     },
28857     
28858     uploadStart : function(file, crop)
28859     {
28860         this.xhr = new XMLHttpRequest();
28861         
28862         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28863             this.arrange();
28864             return;
28865         }
28866         
28867         file.xhr = this.xhr;
28868             
28869         this.managerEl.createChild({
28870             tag : 'div',
28871             cls : 'roo-document-manager-loading',
28872             cn : [
28873                 {
28874                     tag : 'div',
28875                     tooltip : file.name,
28876                     cls : 'roo-document-manager-thumb',
28877                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28878                 }
28879             ]
28880
28881         });
28882
28883         this.xhr.open(this.method, this.url, true);
28884         
28885         var headers = {
28886             "Accept": "application/json",
28887             "Cache-Control": "no-cache",
28888             "X-Requested-With": "XMLHttpRequest"
28889         };
28890         
28891         for (var headerName in headers) {
28892             var headerValue = headers[headerName];
28893             if (headerValue) {
28894                 this.xhr.setRequestHeader(headerName, headerValue);
28895             }
28896         }
28897         
28898         var _this = this;
28899         
28900         this.xhr.onload = function()
28901         {
28902             _this.xhrOnLoad(_this.xhr);
28903         }
28904         
28905         this.xhr.onerror = function()
28906         {
28907             _this.xhrOnError(_this.xhr);
28908         }
28909         
28910         var formData = new FormData();
28911
28912         formData.append('returnHTML', 'NO');
28913         
28914         if(crop){
28915             formData.append('crop', crop);
28916         }
28917         
28918         formData.append(this.paramName, file, file.name);
28919         
28920         var options = {
28921             file : file, 
28922             manually : false
28923         };
28924         
28925         if(this.fireEvent('prepare', this, formData, options) != false){
28926             
28927             if(options.manually){
28928                 return;
28929             }
28930             
28931             this.xhr.send(formData);
28932             return;
28933         };
28934         
28935         this.uploadCancel();
28936     },
28937     
28938     uploadCancel : function()
28939     {
28940         if (this.xhr) {
28941             this.xhr.abort();
28942         }
28943         
28944         this.delegates = [];
28945         
28946         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28947             el.remove();
28948         }, this);
28949         
28950         this.arrange();
28951     },
28952     
28953     renderPreview : function(file)
28954     {
28955         if(typeof(file.target) != 'undefined' && file.target){
28956             return file;
28957         }
28958         
28959         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28960         
28961         var previewEl = this.managerEl.createChild({
28962             tag : 'div',
28963             cls : 'roo-document-manager-preview',
28964             cn : [
28965                 {
28966                     tag : 'div',
28967                     tooltip : file[this.toolTipName],
28968                     cls : 'roo-document-manager-thumb',
28969                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28970                 },
28971                 {
28972                     tag : 'button',
28973                     cls : 'close',
28974                     html : '<i class="fa fa-times-circle"></i>'
28975                 }
28976             ]
28977         });
28978
28979         var close = previewEl.select('button.close', true).first();
28980
28981         close.on('click', this.onRemove, this, file);
28982
28983         file.target = previewEl;
28984
28985         var image = previewEl.select('img', true).first();
28986         
28987         var _this = this;
28988         
28989         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28990         
28991         image.on('click', this.onClick, this, file);
28992         
28993         return file;
28994         
28995     },
28996     
28997     onPreviewLoad : function(file, image)
28998     {
28999         if(typeof(file.target) == 'undefined' || !file.target){
29000             return;
29001         }
29002         
29003         var width = image.dom.naturalWidth || image.dom.width;
29004         var height = image.dom.naturalHeight || image.dom.height;
29005         
29006         if(width > height){
29007             file.target.addClass('wide');
29008             return;
29009         }
29010         
29011         file.target.addClass('tall');
29012         return;
29013         
29014     },
29015     
29016     uploadFromSource : function(file, crop)
29017     {
29018         this.xhr = new XMLHttpRequest();
29019         
29020         this.managerEl.createChild({
29021             tag : 'div',
29022             cls : 'roo-document-manager-loading',
29023             cn : [
29024                 {
29025                     tag : 'div',
29026                     tooltip : file.name,
29027                     cls : 'roo-document-manager-thumb',
29028                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29029                 }
29030             ]
29031
29032         });
29033
29034         this.xhr.open(this.method, this.url, true);
29035         
29036         var headers = {
29037             "Accept": "application/json",
29038             "Cache-Control": "no-cache",
29039             "X-Requested-With": "XMLHttpRequest"
29040         };
29041         
29042         for (var headerName in headers) {
29043             var headerValue = headers[headerName];
29044             if (headerValue) {
29045                 this.xhr.setRequestHeader(headerName, headerValue);
29046             }
29047         }
29048         
29049         var _this = this;
29050         
29051         this.xhr.onload = function()
29052         {
29053             _this.xhrOnLoad(_this.xhr);
29054         }
29055         
29056         this.xhr.onerror = function()
29057         {
29058             _this.xhrOnError(_this.xhr);
29059         }
29060         
29061         var formData = new FormData();
29062
29063         formData.append('returnHTML', 'NO');
29064         
29065         formData.append('crop', crop);
29066         
29067         if(typeof(file.filename) != 'undefined'){
29068             formData.append('filename', file.filename);
29069         }
29070         
29071         if(typeof(file.mimetype) != 'undefined'){
29072             formData.append('mimetype', file.mimetype);
29073         }
29074         
29075         Roo.log(formData);
29076         
29077         if(this.fireEvent('prepare', this, formData) != false){
29078             this.xhr.send(formData);
29079         };
29080     }
29081 });
29082
29083 /*
29084 * Licence: LGPL
29085 */
29086
29087 /**
29088  * @class Roo.bootstrap.DocumentViewer
29089  * @extends Roo.bootstrap.Component
29090  * Bootstrap DocumentViewer class
29091  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29092  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29093  * 
29094  * @constructor
29095  * Create a new DocumentViewer
29096  * @param {Object} config The config object
29097  */
29098
29099 Roo.bootstrap.DocumentViewer = function(config){
29100     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29101     
29102     this.addEvents({
29103         /**
29104          * @event initial
29105          * Fire after initEvent
29106          * @param {Roo.bootstrap.DocumentViewer} this
29107          */
29108         "initial" : true,
29109         /**
29110          * @event click
29111          * Fire after click
29112          * @param {Roo.bootstrap.DocumentViewer} this
29113          */
29114         "click" : true,
29115         /**
29116          * @event download
29117          * Fire after download button
29118          * @param {Roo.bootstrap.DocumentViewer} this
29119          */
29120         "download" : true,
29121         /**
29122          * @event trash
29123          * Fire after trash button
29124          * @param {Roo.bootstrap.DocumentViewer} this
29125          */
29126         "trash" : true
29127         
29128     });
29129 };
29130
29131 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29132     
29133     showDownload : true,
29134     
29135     showTrash : true,
29136     
29137     getAutoCreate : function()
29138     {
29139         var cfg = {
29140             tag : 'div',
29141             cls : 'roo-document-viewer',
29142             cn : [
29143                 {
29144                     tag : 'div',
29145                     cls : 'roo-document-viewer-body',
29146                     cn : [
29147                         {
29148                             tag : 'div',
29149                             cls : 'roo-document-viewer-thumb',
29150                             cn : [
29151                                 {
29152                                     tag : 'img',
29153                                     cls : 'roo-document-viewer-image'
29154                                 }
29155                             ]
29156                         }
29157                     ]
29158                 },
29159                 {
29160                     tag : 'div',
29161                     cls : 'roo-document-viewer-footer',
29162                     cn : {
29163                         tag : 'div',
29164                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29165                         cn : [
29166                             {
29167                                 tag : 'div',
29168                                 cls : 'btn-group roo-document-viewer-download',
29169                                 cn : [
29170                                     {
29171                                         tag : 'button',
29172                                         cls : 'btn btn-default',
29173                                         html : '<i class="fa fa-download"></i>'
29174                                     }
29175                                 ]
29176                             },
29177                             {
29178                                 tag : 'div',
29179                                 cls : 'btn-group roo-document-viewer-trash',
29180                                 cn : [
29181                                     {
29182                                         tag : 'button',
29183                                         cls : 'btn btn-default',
29184                                         html : '<i class="fa fa-trash"></i>'
29185                                     }
29186                                 ]
29187                             }
29188                         ]
29189                     }
29190                 }
29191             ]
29192         };
29193         
29194         return cfg;
29195     },
29196     
29197     initEvents : function()
29198     {
29199         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29200         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29201         
29202         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29203         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29204         
29205         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29206         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29207         
29208         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29209         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29210         
29211         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29212         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29213         
29214         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29215         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29216         
29217         this.bodyEl.on('click', this.onClick, this);
29218         this.downloadBtn.on('click', this.onDownload, this);
29219         this.trashBtn.on('click', this.onTrash, this);
29220         
29221         this.downloadBtn.hide();
29222         this.trashBtn.hide();
29223         
29224         if(this.showDownload){
29225             this.downloadBtn.show();
29226         }
29227         
29228         if(this.showTrash){
29229             this.trashBtn.show();
29230         }
29231         
29232         if(!this.showDownload && !this.showTrash) {
29233             this.footerEl.hide();
29234         }
29235         
29236     },
29237     
29238     initial : function()
29239     {
29240         this.fireEvent('initial', this);
29241         
29242     },
29243     
29244     onClick : function(e)
29245     {
29246         e.preventDefault();
29247         
29248         this.fireEvent('click', this);
29249     },
29250     
29251     onDownload : function(e)
29252     {
29253         e.preventDefault();
29254         
29255         this.fireEvent('download', this);
29256     },
29257     
29258     onTrash : function(e)
29259     {
29260         e.preventDefault();
29261         
29262         this.fireEvent('trash', this);
29263     }
29264     
29265 });
29266 /*
29267  * - LGPL
29268  *
29269  * nav progress bar
29270  * 
29271  */
29272
29273 /**
29274  * @class Roo.bootstrap.NavProgressBar
29275  * @extends Roo.bootstrap.Component
29276  * Bootstrap NavProgressBar class
29277  * 
29278  * @constructor
29279  * Create a new nav progress bar
29280  * @param {Object} config The config object
29281  */
29282
29283 Roo.bootstrap.NavProgressBar = function(config){
29284     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29285
29286     this.bullets = this.bullets || [];
29287    
29288 //    Roo.bootstrap.NavProgressBar.register(this);
29289      this.addEvents({
29290         /**
29291              * @event changed
29292              * Fires when the active item changes
29293              * @param {Roo.bootstrap.NavProgressBar} this
29294              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29295              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29296          */
29297         'changed': true
29298      });
29299     
29300 };
29301
29302 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29303     
29304     bullets : [],
29305     barItems : [],
29306     
29307     getAutoCreate : function()
29308     {
29309         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29310         
29311         cfg = {
29312             tag : 'div',
29313             cls : 'roo-navigation-bar-group',
29314             cn : [
29315                 {
29316                     tag : 'div',
29317                     cls : 'roo-navigation-top-bar'
29318                 },
29319                 {
29320                     tag : 'div',
29321                     cls : 'roo-navigation-bullets-bar',
29322                     cn : [
29323                         {
29324                             tag : 'ul',
29325                             cls : 'roo-navigation-bar'
29326                         }
29327                     ]
29328                 },
29329                 
29330                 {
29331                     tag : 'div',
29332                     cls : 'roo-navigation-bottom-bar'
29333                 }
29334             ]
29335             
29336         };
29337         
29338         return cfg;
29339         
29340     },
29341     
29342     initEvents: function() 
29343     {
29344         
29345     },
29346     
29347     onRender : function(ct, position) 
29348     {
29349         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29350         
29351         if(this.bullets.length){
29352             Roo.each(this.bullets, function(b){
29353                this.addItem(b);
29354             }, this);
29355         }
29356         
29357         this.format();
29358         
29359     },
29360     
29361     addItem : function(cfg)
29362     {
29363         var item = new Roo.bootstrap.NavProgressItem(cfg);
29364         
29365         item.parentId = this.id;
29366         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29367         
29368         if(cfg.html){
29369             var top = new Roo.bootstrap.Element({
29370                 tag : 'div',
29371                 cls : 'roo-navigation-bar-text'
29372             });
29373             
29374             var bottom = new Roo.bootstrap.Element({
29375                 tag : 'div',
29376                 cls : 'roo-navigation-bar-text'
29377             });
29378             
29379             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29380             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29381             
29382             var topText = new Roo.bootstrap.Element({
29383                 tag : 'span',
29384                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29385             });
29386             
29387             var bottomText = new Roo.bootstrap.Element({
29388                 tag : 'span',
29389                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29390             });
29391             
29392             topText.onRender(top.el, null);
29393             bottomText.onRender(bottom.el, null);
29394             
29395             item.topEl = top;
29396             item.bottomEl = bottom;
29397         }
29398         
29399         this.barItems.push(item);
29400         
29401         return item;
29402     },
29403     
29404     getActive : function()
29405     {
29406         var active = false;
29407         
29408         Roo.each(this.barItems, function(v){
29409             
29410             if (!v.isActive()) {
29411                 return;
29412             }
29413             
29414             active = v;
29415             return false;
29416             
29417         });
29418         
29419         return active;
29420     },
29421     
29422     setActiveItem : function(item)
29423     {
29424         var prev = false;
29425         
29426         Roo.each(this.barItems, function(v){
29427             if (v.rid == item.rid) {
29428                 return ;
29429             }
29430             
29431             if (v.isActive()) {
29432                 v.setActive(false);
29433                 prev = v;
29434             }
29435         });
29436
29437         item.setActive(true);
29438         
29439         this.fireEvent('changed', this, item, prev);
29440     },
29441     
29442     getBarItem: function(rid)
29443     {
29444         var ret = false;
29445         
29446         Roo.each(this.barItems, function(e) {
29447             if (e.rid != rid) {
29448                 return;
29449             }
29450             
29451             ret =  e;
29452             return false;
29453         });
29454         
29455         return ret;
29456     },
29457     
29458     indexOfItem : function(item)
29459     {
29460         var index = false;
29461         
29462         Roo.each(this.barItems, function(v, i){
29463             
29464             if (v.rid != item.rid) {
29465                 return;
29466             }
29467             
29468             index = i;
29469             return false
29470         });
29471         
29472         return index;
29473     },
29474     
29475     setActiveNext : function()
29476     {
29477         var i = this.indexOfItem(this.getActive());
29478         
29479         if (i > this.barItems.length) {
29480             return;
29481         }
29482         
29483         this.setActiveItem(this.barItems[i+1]);
29484     },
29485     
29486     setActivePrev : function()
29487     {
29488         var i = this.indexOfItem(this.getActive());
29489         
29490         if (i  < 1) {
29491             return;
29492         }
29493         
29494         this.setActiveItem(this.barItems[i-1]);
29495     },
29496     
29497     format : function()
29498     {
29499         if(!this.barItems.length){
29500             return;
29501         }
29502      
29503         var width = 100 / this.barItems.length;
29504         
29505         Roo.each(this.barItems, function(i){
29506             i.el.setStyle('width', width + '%');
29507             i.topEl.el.setStyle('width', width + '%');
29508             i.bottomEl.el.setStyle('width', width + '%');
29509         }, this);
29510         
29511     }
29512     
29513 });
29514 /*
29515  * - LGPL
29516  *
29517  * Nav Progress Item
29518  * 
29519  */
29520
29521 /**
29522  * @class Roo.bootstrap.NavProgressItem
29523  * @extends Roo.bootstrap.Component
29524  * Bootstrap NavProgressItem class
29525  * @cfg {String} rid the reference id
29526  * @cfg {Boolean} active (true|false) Is item active default false
29527  * @cfg {Boolean} disabled (true|false) Is item active default false
29528  * @cfg {String} html
29529  * @cfg {String} position (top|bottom) text position default bottom
29530  * @cfg {String} icon show icon instead of number
29531  * 
29532  * @constructor
29533  * Create a new NavProgressItem
29534  * @param {Object} config The config object
29535  */
29536 Roo.bootstrap.NavProgressItem = function(config){
29537     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29538     this.addEvents({
29539         // raw events
29540         /**
29541          * @event click
29542          * The raw click event for the entire grid.
29543          * @param {Roo.bootstrap.NavProgressItem} this
29544          * @param {Roo.EventObject} e
29545          */
29546         "click" : true
29547     });
29548    
29549 };
29550
29551 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29552     
29553     rid : '',
29554     active : false,
29555     disabled : false,
29556     html : '',
29557     position : 'bottom',
29558     icon : false,
29559     
29560     getAutoCreate : function()
29561     {
29562         var iconCls = 'roo-navigation-bar-item-icon';
29563         
29564         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29565         
29566         var cfg = {
29567             tag: 'li',
29568             cls: 'roo-navigation-bar-item',
29569             cn : [
29570                 {
29571                     tag : 'i',
29572                     cls : iconCls
29573                 }
29574             ]
29575         };
29576         
29577         if(this.active){
29578             cfg.cls += ' active';
29579         }
29580         if(this.disabled){
29581             cfg.cls += ' disabled';
29582         }
29583         
29584         return cfg;
29585     },
29586     
29587     disable : function()
29588     {
29589         this.setDisabled(true);
29590     },
29591     
29592     enable : function()
29593     {
29594         this.setDisabled(false);
29595     },
29596     
29597     initEvents: function() 
29598     {
29599         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29600         
29601         this.iconEl.on('click', this.onClick, this);
29602     },
29603     
29604     onClick : function(e)
29605     {
29606         e.preventDefault();
29607         
29608         if(this.disabled){
29609             return;
29610         }
29611         
29612         if(this.fireEvent('click', this, e) === false){
29613             return;
29614         };
29615         
29616         this.parent().setActiveItem(this);
29617     },
29618     
29619     isActive: function () 
29620     {
29621         return this.active;
29622     },
29623     
29624     setActive : function(state)
29625     {
29626         if(this.active == state){
29627             return;
29628         }
29629         
29630         this.active = state;
29631         
29632         if (state) {
29633             this.el.addClass('active');
29634             return;
29635         }
29636         
29637         this.el.removeClass('active');
29638         
29639         return;
29640     },
29641     
29642     setDisabled : function(state)
29643     {
29644         if(this.disabled == state){
29645             return;
29646         }
29647         
29648         this.disabled = state;
29649         
29650         if (state) {
29651             this.el.addClass('disabled');
29652             return;
29653         }
29654         
29655         this.el.removeClass('disabled');
29656     },
29657     
29658     tooltipEl : function()
29659     {
29660         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29661     }
29662 });
29663  
29664
29665  /*
29666  * - LGPL
29667  *
29668  * FieldLabel
29669  * 
29670  */
29671
29672 /**
29673  * @class Roo.bootstrap.FieldLabel
29674  * @extends Roo.bootstrap.Component
29675  * Bootstrap FieldLabel class
29676  * @cfg {String} html contents of the element
29677  * @cfg {String} tag tag of the element default label
29678  * @cfg {String} cls class of the element
29679  * @cfg {String} target label target 
29680  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29681  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29682  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29683  * @cfg {String} iconTooltip default "This field is required"
29684  * 
29685  * @constructor
29686  * Create a new FieldLabel
29687  * @param {Object} config The config object
29688  */
29689
29690 Roo.bootstrap.FieldLabel = function(config){
29691     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29692     
29693     this.addEvents({
29694             /**
29695              * @event invalid
29696              * Fires after the field has been marked as invalid.
29697              * @param {Roo.form.FieldLabel} this
29698              * @param {String} msg The validation message
29699              */
29700             invalid : true,
29701             /**
29702              * @event valid
29703              * Fires after the field has been validated with no errors.
29704              * @param {Roo.form.FieldLabel} this
29705              */
29706             valid : true
29707         });
29708 };
29709
29710 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29711     
29712     tag: 'label',
29713     cls: '',
29714     html: '',
29715     target: '',
29716     allowBlank : true,
29717     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29718     validClass : 'text-success fa fa-lg fa-check',
29719     iconTooltip : 'This field is required',
29720     
29721     getAutoCreate : function(){
29722         
29723         var cfg = {
29724             tag : this.tag,
29725             cls : 'roo-bootstrap-field-label ' + this.cls,
29726             for : this.target,
29727             cn : [
29728                 {
29729                     tag : 'i',
29730                     cls : '',
29731                     tooltip : this.iconTooltip
29732                 },
29733                 {
29734                     tag : 'span',
29735                     html : this.html
29736                 }
29737             ] 
29738         };
29739         
29740         return cfg;
29741     },
29742     
29743     initEvents: function() 
29744     {
29745         Roo.bootstrap.Element.superclass.initEvents.call(this);
29746         
29747         this.iconEl = this.el.select('i', true).first();
29748         
29749         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29750         
29751         Roo.bootstrap.FieldLabel.register(this);
29752     },
29753     
29754     /**
29755      * Mark this field as valid
29756      */
29757     markValid : function()
29758     {
29759         this.iconEl.show();
29760         
29761         this.iconEl.removeClass(this.invalidClass);
29762         
29763         this.iconEl.addClass(this.validClass);
29764         
29765         this.fireEvent('valid', this);
29766     },
29767     
29768     /**
29769      * Mark this field as invalid
29770      * @param {String} msg The validation message
29771      */
29772     markInvalid : function(msg)
29773     {
29774         this.iconEl.show();
29775         
29776         this.iconEl.removeClass(this.validClass);
29777         
29778         this.iconEl.addClass(this.invalidClass);
29779         
29780         this.fireEvent('invalid', this, msg);
29781     }
29782     
29783    
29784 });
29785
29786 Roo.apply(Roo.bootstrap.FieldLabel, {
29787     
29788     groups: {},
29789     
29790      /**
29791     * register a FieldLabel Group
29792     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29793     */
29794     register : function(label)
29795     {
29796         if(this.groups.hasOwnProperty(label.target)){
29797             return;
29798         }
29799      
29800         this.groups[label.target] = label;
29801         
29802     },
29803     /**
29804     * fetch a FieldLabel Group based on the target
29805     * @param {string} target
29806     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29807     */
29808     get: function(target) {
29809         if (typeof(this.groups[target]) == 'undefined') {
29810             return false;
29811         }
29812         
29813         return this.groups[target] ;
29814     }
29815 });
29816
29817  
29818
29819  /*
29820  * - LGPL
29821  *
29822  * page DateSplitField.
29823  * 
29824  */
29825
29826
29827 /**
29828  * @class Roo.bootstrap.DateSplitField
29829  * @extends Roo.bootstrap.Component
29830  * Bootstrap DateSplitField class
29831  * @cfg {string} fieldLabel - the label associated
29832  * @cfg {Number} labelWidth set the width of label (0-12)
29833  * @cfg {String} labelAlign (top|left)
29834  * @cfg {Boolean} dayAllowBlank (true|false) default false
29835  * @cfg {Boolean} monthAllowBlank (true|false) default false
29836  * @cfg {Boolean} yearAllowBlank (true|false) default false
29837  * @cfg {string} dayPlaceholder 
29838  * @cfg {string} monthPlaceholder
29839  * @cfg {string} yearPlaceholder
29840  * @cfg {string} dayFormat default 'd'
29841  * @cfg {string} monthFormat default 'm'
29842  * @cfg {string} yearFormat default 'Y'
29843  * @cfg {Number} labellg set the width of label (1-12)
29844  * @cfg {Number} labelmd set the width of label (1-12)
29845  * @cfg {Number} labelsm set the width of label (1-12)
29846  * @cfg {Number} labelxs set the width of label (1-12)
29847
29848  *     
29849  * @constructor
29850  * Create a new DateSplitField
29851  * @param {Object} config The config object
29852  */
29853
29854 Roo.bootstrap.DateSplitField = function(config){
29855     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29856     
29857     this.addEvents({
29858         // raw events
29859          /**
29860          * @event years
29861          * getting the data of years
29862          * @param {Roo.bootstrap.DateSplitField} this
29863          * @param {Object} years
29864          */
29865         "years" : true,
29866         /**
29867          * @event days
29868          * getting the data of days
29869          * @param {Roo.bootstrap.DateSplitField} this
29870          * @param {Object} days
29871          */
29872         "days" : true,
29873         /**
29874          * @event invalid
29875          * Fires after the field has been marked as invalid.
29876          * @param {Roo.form.Field} this
29877          * @param {String} msg The validation message
29878          */
29879         invalid : true,
29880        /**
29881          * @event valid
29882          * Fires after the field has been validated with no errors.
29883          * @param {Roo.form.Field} this
29884          */
29885         valid : true
29886     });
29887 };
29888
29889 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29890     
29891     fieldLabel : '',
29892     labelAlign : 'top',
29893     labelWidth : 3,
29894     dayAllowBlank : false,
29895     monthAllowBlank : false,
29896     yearAllowBlank : false,
29897     dayPlaceholder : '',
29898     monthPlaceholder : '',
29899     yearPlaceholder : '',
29900     dayFormat : 'd',
29901     monthFormat : 'm',
29902     yearFormat : 'Y',
29903     isFormField : true,
29904     labellg : 0,
29905     labelmd : 0,
29906     labelsm : 0,
29907     labelxs : 0,
29908     
29909     getAutoCreate : function()
29910     {
29911         var cfg = {
29912             tag : 'div',
29913             cls : 'row roo-date-split-field-group',
29914             cn : [
29915                 {
29916                     tag : 'input',
29917                     type : 'hidden',
29918                     cls : 'form-hidden-field roo-date-split-field-group-value',
29919                     name : this.name
29920                 }
29921             ]
29922         };
29923         
29924         var labelCls = 'col-md-12';
29925         var contentCls = 'col-md-4';
29926         
29927         if(this.fieldLabel){
29928             
29929             var label = {
29930                 tag : 'div',
29931                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29932                 cn : [
29933                     {
29934                         tag : 'label',
29935                         html : this.fieldLabel
29936                     }
29937                 ]
29938             };
29939             
29940             if(this.labelAlign == 'left'){
29941             
29942                 if(this.labelWidth > 12){
29943                     label.style = "width: " + this.labelWidth + 'px';
29944                 }
29945
29946                 if(this.labelWidth < 13 && this.labelmd == 0){
29947                     this.labelmd = this.labelWidth;
29948                 }
29949
29950                 if(this.labellg > 0){
29951                     labelCls = ' col-lg-' + this.labellg;
29952                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29953                 }
29954
29955                 if(this.labelmd > 0){
29956                     labelCls = ' col-md-' + this.labelmd;
29957                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29958                 }
29959
29960                 if(this.labelsm > 0){
29961                     labelCls = ' col-sm-' + this.labelsm;
29962                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29963                 }
29964
29965                 if(this.labelxs > 0){
29966                     labelCls = ' col-xs-' + this.labelxs;
29967                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29968                 }
29969             }
29970             
29971             label.cls += ' ' + labelCls;
29972             
29973             cfg.cn.push(label);
29974         }
29975         
29976         Roo.each(['day', 'month', 'year'], function(t){
29977             cfg.cn.push({
29978                 tag : 'div',
29979                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29980             });
29981         }, this);
29982         
29983         return cfg;
29984     },
29985     
29986     inputEl: function ()
29987     {
29988         return this.el.select('.roo-date-split-field-group-value', true).first();
29989     },
29990     
29991     onRender : function(ct, position) 
29992     {
29993         var _this = this;
29994         
29995         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29996         
29997         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29998         
29999         this.dayField = new Roo.bootstrap.ComboBox({
30000             allowBlank : this.dayAllowBlank,
30001             alwaysQuery : true,
30002             displayField : 'value',
30003             editable : false,
30004             fieldLabel : '',
30005             forceSelection : true,
30006             mode : 'local',
30007             placeholder : this.dayPlaceholder,
30008             selectOnFocus : true,
30009             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30010             triggerAction : 'all',
30011             typeAhead : true,
30012             valueField : 'value',
30013             store : new Roo.data.SimpleStore({
30014                 data : (function() {    
30015                     var days = [];
30016                     _this.fireEvent('days', _this, days);
30017                     return days;
30018                 })(),
30019                 fields : [ 'value' ]
30020             }),
30021             listeners : {
30022                 select : function (_self, record, index)
30023                 {
30024                     _this.setValue(_this.getValue());
30025                 }
30026             }
30027         });
30028
30029         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30030         
30031         this.monthField = new Roo.bootstrap.MonthField({
30032             after : '<i class=\"fa fa-calendar\"></i>',
30033             allowBlank : this.monthAllowBlank,
30034             placeholder : this.monthPlaceholder,
30035             readOnly : true,
30036             listeners : {
30037                 render : function (_self)
30038                 {
30039                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30040                         e.preventDefault();
30041                         _self.focus();
30042                     });
30043                 },
30044                 select : function (_self, oldvalue, newvalue)
30045                 {
30046                     _this.setValue(_this.getValue());
30047                 }
30048             }
30049         });
30050         
30051         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30052         
30053         this.yearField = new Roo.bootstrap.ComboBox({
30054             allowBlank : this.yearAllowBlank,
30055             alwaysQuery : true,
30056             displayField : 'value',
30057             editable : false,
30058             fieldLabel : '',
30059             forceSelection : true,
30060             mode : 'local',
30061             placeholder : this.yearPlaceholder,
30062             selectOnFocus : true,
30063             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30064             triggerAction : 'all',
30065             typeAhead : true,
30066             valueField : 'value',
30067             store : new Roo.data.SimpleStore({
30068                 data : (function() {
30069                     var years = [];
30070                     _this.fireEvent('years', _this, years);
30071                     return years;
30072                 })(),
30073                 fields : [ 'value' ]
30074             }),
30075             listeners : {
30076                 select : function (_self, record, index)
30077                 {
30078                     _this.setValue(_this.getValue());
30079                 }
30080             }
30081         });
30082
30083         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30084     },
30085     
30086     setValue : function(v, format)
30087     {
30088         this.inputEl.dom.value = v;
30089         
30090         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30091         
30092         var d = Date.parseDate(v, f);
30093         
30094         if(!d){
30095             this.validate();
30096             return;
30097         }
30098         
30099         this.setDay(d.format(this.dayFormat));
30100         this.setMonth(d.format(this.monthFormat));
30101         this.setYear(d.format(this.yearFormat));
30102         
30103         this.validate();
30104         
30105         return;
30106     },
30107     
30108     setDay : function(v)
30109     {
30110         this.dayField.setValue(v);
30111         this.inputEl.dom.value = this.getValue();
30112         this.validate();
30113         return;
30114     },
30115     
30116     setMonth : function(v)
30117     {
30118         this.monthField.setValue(v, true);
30119         this.inputEl.dom.value = this.getValue();
30120         this.validate();
30121         return;
30122     },
30123     
30124     setYear : function(v)
30125     {
30126         this.yearField.setValue(v);
30127         this.inputEl.dom.value = this.getValue();
30128         this.validate();
30129         return;
30130     },
30131     
30132     getDay : function()
30133     {
30134         return this.dayField.getValue();
30135     },
30136     
30137     getMonth : function()
30138     {
30139         return this.monthField.getValue();
30140     },
30141     
30142     getYear : function()
30143     {
30144         return this.yearField.getValue();
30145     },
30146     
30147     getValue : function()
30148     {
30149         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30150         
30151         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30152         
30153         return date;
30154     },
30155     
30156     reset : function()
30157     {
30158         this.setDay('');
30159         this.setMonth('');
30160         this.setYear('');
30161         this.inputEl.dom.value = '';
30162         this.validate();
30163         return;
30164     },
30165     
30166     validate : function()
30167     {
30168         var d = this.dayField.validate();
30169         var m = this.monthField.validate();
30170         var y = this.yearField.validate();
30171         
30172         var valid = true;
30173         
30174         if(
30175                 (!this.dayAllowBlank && !d) ||
30176                 (!this.monthAllowBlank && !m) ||
30177                 (!this.yearAllowBlank && !y)
30178         ){
30179             valid = false;
30180         }
30181         
30182         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30183             return valid;
30184         }
30185         
30186         if(valid){
30187             this.markValid();
30188             return valid;
30189         }
30190         
30191         this.markInvalid();
30192         
30193         return valid;
30194     },
30195     
30196     markValid : function()
30197     {
30198         
30199         var label = this.el.select('label', true).first();
30200         var icon = this.el.select('i.fa-star', true).first();
30201
30202         if(label && icon){
30203             icon.remove();
30204         }
30205         
30206         this.fireEvent('valid', this);
30207     },
30208     
30209      /**
30210      * Mark this field as invalid
30211      * @param {String} msg The validation message
30212      */
30213     markInvalid : function(msg)
30214     {
30215         
30216         var label = this.el.select('label', true).first();
30217         var icon = this.el.select('i.fa-star', true).first();
30218
30219         if(label && !icon){
30220             this.el.select('.roo-date-split-field-label', true).createChild({
30221                 tag : 'i',
30222                 cls : 'text-danger fa fa-lg fa-star',
30223                 tooltip : 'This field is required',
30224                 style : 'margin-right:5px;'
30225             }, label, true);
30226         }
30227         
30228         this.fireEvent('invalid', this, msg);
30229     },
30230     
30231     clearInvalid : function()
30232     {
30233         var label = this.el.select('label', true).first();
30234         var icon = this.el.select('i.fa-star', true).first();
30235
30236         if(label && icon){
30237             icon.remove();
30238         }
30239         
30240         this.fireEvent('valid', this);
30241     },
30242     
30243     getName: function()
30244     {
30245         return this.name;
30246     }
30247     
30248 });
30249
30250  /**
30251  *
30252  * This is based on 
30253  * http://masonry.desandro.com
30254  *
30255  * The idea is to render all the bricks based on vertical width...
30256  *
30257  * The original code extends 'outlayer' - we might need to use that....
30258  * 
30259  */
30260
30261
30262 /**
30263  * @class Roo.bootstrap.LayoutMasonry
30264  * @extends Roo.bootstrap.Component
30265  * Bootstrap Layout Masonry class
30266  * 
30267  * @constructor
30268  * Create a new Element
30269  * @param {Object} config The config object
30270  */
30271
30272 Roo.bootstrap.LayoutMasonry = function(config){
30273     
30274     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30275     
30276     this.bricks = [];
30277     
30278     Roo.bootstrap.LayoutMasonry.register(this);
30279     
30280     this.addEvents({
30281         // raw events
30282         /**
30283          * @event layout
30284          * Fire after layout the items
30285          * @param {Roo.bootstrap.LayoutMasonry} this
30286          * @param {Roo.EventObject} e
30287          */
30288         "layout" : true
30289     });
30290     
30291 };
30292
30293 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30294     
30295     /**
30296      * @cfg {Boolean} isLayoutInstant = no animation?
30297      */   
30298     isLayoutInstant : false, // needed?
30299    
30300     /**
30301      * @cfg {Number} boxWidth  width of the columns
30302      */   
30303     boxWidth : 450,
30304     
30305       /**
30306      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30307      */   
30308     boxHeight : 0,
30309     
30310     /**
30311      * @cfg {Number} padWidth padding below box..
30312      */   
30313     padWidth : 10, 
30314     
30315     /**
30316      * @cfg {Number} gutter gutter width..
30317      */   
30318     gutter : 10,
30319     
30320      /**
30321      * @cfg {Number} maxCols maximum number of columns
30322      */   
30323     
30324     maxCols: 0,
30325     
30326     /**
30327      * @cfg {Boolean} isAutoInitial defalut true
30328      */   
30329     isAutoInitial : true, 
30330     
30331     containerWidth: 0,
30332     
30333     /**
30334      * @cfg {Boolean} isHorizontal defalut false
30335      */   
30336     isHorizontal : false, 
30337
30338     currentSize : null,
30339     
30340     tag: 'div',
30341     
30342     cls: '',
30343     
30344     bricks: null, //CompositeElement
30345     
30346     cols : 1,
30347     
30348     _isLayoutInited : false,
30349     
30350 //    isAlternative : false, // only use for vertical layout...
30351     
30352     /**
30353      * @cfg {Number} alternativePadWidth padding below box..
30354      */   
30355     alternativePadWidth : 50,
30356     
30357     selectedBrick : [],
30358     
30359     getAutoCreate : function(){
30360         
30361         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30362         
30363         var cfg = {
30364             tag: this.tag,
30365             cls: 'blog-masonary-wrapper ' + this.cls,
30366             cn : {
30367                 cls : 'mas-boxes masonary'
30368             }
30369         };
30370         
30371         return cfg;
30372     },
30373     
30374     getChildContainer: function( )
30375     {
30376         if (this.boxesEl) {
30377             return this.boxesEl;
30378         }
30379         
30380         this.boxesEl = this.el.select('.mas-boxes').first();
30381         
30382         return this.boxesEl;
30383     },
30384     
30385     
30386     initEvents : function()
30387     {
30388         var _this = this;
30389         
30390         if(this.isAutoInitial){
30391             Roo.log('hook children rendered');
30392             this.on('childrenrendered', function() {
30393                 Roo.log('children rendered');
30394                 _this.initial();
30395             } ,this);
30396         }
30397     },
30398     
30399     initial : function()
30400     {
30401         this.selectedBrick = [];
30402         
30403         this.currentSize = this.el.getBox(true);
30404         
30405         Roo.EventManager.onWindowResize(this.resize, this); 
30406
30407         if(!this.isAutoInitial){
30408             this.layout();
30409             return;
30410         }
30411         
30412         this.layout();
30413         
30414         return;
30415         //this.layout.defer(500,this);
30416         
30417     },
30418     
30419     resize : function()
30420     {
30421         var cs = this.el.getBox(true);
30422         
30423         if (
30424                 this.currentSize.width == cs.width && 
30425                 this.currentSize.x == cs.x && 
30426                 this.currentSize.height == cs.height && 
30427                 this.currentSize.y == cs.y 
30428         ) {
30429             Roo.log("no change in with or X or Y");
30430             return;
30431         }
30432         
30433         this.currentSize = cs;
30434         
30435         this.layout();
30436         
30437     },
30438     
30439     layout : function()
30440     {   
30441         this._resetLayout();
30442         
30443         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30444         
30445         this.layoutItems( isInstant );
30446       
30447         this._isLayoutInited = true;
30448         
30449         this.fireEvent('layout', this);
30450         
30451     },
30452     
30453     _resetLayout : function()
30454     {
30455         if(this.isHorizontal){
30456             this.horizontalMeasureColumns();
30457             return;
30458         }
30459         
30460         this.verticalMeasureColumns();
30461         
30462     },
30463     
30464     verticalMeasureColumns : function()
30465     {
30466         this.getContainerWidth();
30467         
30468 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30469 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30470 //            return;
30471 //        }
30472         
30473         var boxWidth = this.boxWidth + this.padWidth;
30474         
30475         if(this.containerWidth < this.boxWidth){
30476             boxWidth = this.containerWidth
30477         }
30478         
30479         var containerWidth = this.containerWidth;
30480         
30481         var cols = Math.floor(containerWidth / boxWidth);
30482         
30483         this.cols = Math.max( cols, 1 );
30484         
30485         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30486         
30487         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30488         
30489         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30490         
30491         this.colWidth = boxWidth + avail - this.padWidth;
30492         
30493         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30494         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30495     },
30496     
30497     horizontalMeasureColumns : function()
30498     {
30499         this.getContainerWidth();
30500         
30501         var boxWidth = this.boxWidth;
30502         
30503         if(this.containerWidth < boxWidth){
30504             boxWidth = this.containerWidth;
30505         }
30506         
30507         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30508         
30509         this.el.setHeight(boxWidth);
30510         
30511     },
30512     
30513     getContainerWidth : function()
30514     {
30515         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30516     },
30517     
30518     layoutItems : function( isInstant )
30519     {
30520         Roo.log(this.bricks);
30521         
30522         var items = Roo.apply([], this.bricks);
30523         
30524         if(this.isHorizontal){
30525             this._horizontalLayoutItems( items , isInstant );
30526             return;
30527         }
30528         
30529 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30530 //            this._verticalAlternativeLayoutItems( items , isInstant );
30531 //            return;
30532 //        }
30533         
30534         this._verticalLayoutItems( items , isInstant );
30535         
30536     },
30537     
30538     _verticalLayoutItems : function ( items , isInstant)
30539     {
30540         if ( !items || !items.length ) {
30541             return;
30542         }
30543         
30544         var standard = [
30545             ['xs', 'xs', 'xs', 'tall'],
30546             ['xs', 'xs', 'tall'],
30547             ['xs', 'xs', 'sm'],
30548             ['xs', 'xs', 'xs'],
30549             ['xs', 'tall'],
30550             ['xs', 'sm'],
30551             ['xs', 'xs'],
30552             ['xs'],
30553             
30554             ['sm', 'xs', 'xs'],
30555             ['sm', 'xs'],
30556             ['sm'],
30557             
30558             ['tall', 'xs', 'xs', 'xs'],
30559             ['tall', 'xs', 'xs'],
30560             ['tall', 'xs'],
30561             ['tall']
30562             
30563         ];
30564         
30565         var queue = [];
30566         
30567         var boxes = [];
30568         
30569         var box = [];
30570         
30571         Roo.each(items, function(item, k){
30572             
30573             switch (item.size) {
30574                 // these layouts take up a full box,
30575                 case 'md' :
30576                 case 'md-left' :
30577                 case 'md-right' :
30578                 case 'wide' :
30579                     
30580                     if(box.length){
30581                         boxes.push(box);
30582                         box = [];
30583                     }
30584                     
30585                     boxes.push([item]);
30586                     
30587                     break;
30588                     
30589                 case 'xs' :
30590                 case 'sm' :
30591                 case 'tall' :
30592                     
30593                     box.push(item);
30594                     
30595                     break;
30596                 default :
30597                     break;
30598                     
30599             }
30600             
30601         }, this);
30602         
30603         if(box.length){
30604             boxes.push(box);
30605             box = [];
30606         }
30607         
30608         var filterPattern = function(box, length)
30609         {
30610             if(!box.length){
30611                 return;
30612             }
30613             
30614             var match = false;
30615             
30616             var pattern = box.slice(0, length);
30617             
30618             var format = [];
30619             
30620             Roo.each(pattern, function(i){
30621                 format.push(i.size);
30622             }, this);
30623             
30624             Roo.each(standard, function(s){
30625                 
30626                 if(String(s) != String(format)){
30627                     return;
30628                 }
30629                 
30630                 match = true;
30631                 return false;
30632                 
30633             }, this);
30634             
30635             if(!match && length == 1){
30636                 return;
30637             }
30638             
30639             if(!match){
30640                 filterPattern(box, length - 1);
30641                 return;
30642             }
30643                 
30644             queue.push(pattern);
30645
30646             box = box.slice(length, box.length);
30647
30648             filterPattern(box, 4);
30649
30650             return;
30651             
30652         }
30653         
30654         Roo.each(boxes, function(box, k){
30655             
30656             if(!box.length){
30657                 return;
30658             }
30659             
30660             if(box.length == 1){
30661                 queue.push(box);
30662                 return;
30663             }
30664             
30665             filterPattern(box, 4);
30666             
30667         }, this);
30668         
30669         this._processVerticalLayoutQueue( queue, isInstant );
30670         
30671     },
30672     
30673 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30674 //    {
30675 //        if ( !items || !items.length ) {
30676 //            return;
30677 //        }
30678 //
30679 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30680 //        
30681 //    },
30682     
30683     _horizontalLayoutItems : function ( items , isInstant)
30684     {
30685         if ( !items || !items.length || items.length < 3) {
30686             return;
30687         }
30688         
30689         items.reverse();
30690         
30691         var eItems = items.slice(0, 3);
30692         
30693         items = items.slice(3, items.length);
30694         
30695         var standard = [
30696             ['xs', 'xs', 'xs', 'wide'],
30697             ['xs', 'xs', 'wide'],
30698             ['xs', 'xs', 'sm'],
30699             ['xs', 'xs', 'xs'],
30700             ['xs', 'wide'],
30701             ['xs', 'sm'],
30702             ['xs', 'xs'],
30703             ['xs'],
30704             
30705             ['sm', 'xs', 'xs'],
30706             ['sm', 'xs'],
30707             ['sm'],
30708             
30709             ['wide', 'xs', 'xs', 'xs'],
30710             ['wide', 'xs', 'xs'],
30711             ['wide', 'xs'],
30712             ['wide'],
30713             
30714             ['wide-thin']
30715         ];
30716         
30717         var queue = [];
30718         
30719         var boxes = [];
30720         
30721         var box = [];
30722         
30723         Roo.each(items, function(item, k){
30724             
30725             switch (item.size) {
30726                 case 'md' :
30727                 case 'md-left' :
30728                 case 'md-right' :
30729                 case 'tall' :
30730                     
30731                     if(box.length){
30732                         boxes.push(box);
30733                         box = [];
30734                     }
30735                     
30736                     boxes.push([item]);
30737                     
30738                     break;
30739                     
30740                 case 'xs' :
30741                 case 'sm' :
30742                 case 'wide' :
30743                 case 'wide-thin' :
30744                     
30745                     box.push(item);
30746                     
30747                     break;
30748                 default :
30749                     break;
30750                     
30751             }
30752             
30753         }, this);
30754         
30755         if(box.length){
30756             boxes.push(box);
30757             box = [];
30758         }
30759         
30760         var filterPattern = function(box, length)
30761         {
30762             if(!box.length){
30763                 return;
30764             }
30765             
30766             var match = false;
30767             
30768             var pattern = box.slice(0, length);
30769             
30770             var format = [];
30771             
30772             Roo.each(pattern, function(i){
30773                 format.push(i.size);
30774             }, this);
30775             
30776             Roo.each(standard, function(s){
30777                 
30778                 if(String(s) != String(format)){
30779                     return;
30780                 }
30781                 
30782                 match = true;
30783                 return false;
30784                 
30785             }, this);
30786             
30787             if(!match && length == 1){
30788                 return;
30789             }
30790             
30791             if(!match){
30792                 filterPattern(box, length - 1);
30793                 return;
30794             }
30795                 
30796             queue.push(pattern);
30797
30798             box = box.slice(length, box.length);
30799
30800             filterPattern(box, 4);
30801
30802             return;
30803             
30804         }
30805         
30806         Roo.each(boxes, function(box, k){
30807             
30808             if(!box.length){
30809                 return;
30810             }
30811             
30812             if(box.length == 1){
30813                 queue.push(box);
30814                 return;
30815             }
30816             
30817             filterPattern(box, 4);
30818             
30819         }, this);
30820         
30821         
30822         var prune = [];
30823         
30824         var pos = this.el.getBox(true);
30825         
30826         var minX = pos.x;
30827         
30828         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30829         
30830         var hit_end = false;
30831         
30832         Roo.each(queue, function(box){
30833             
30834             if(hit_end){
30835                 
30836                 Roo.each(box, function(b){
30837                 
30838                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30839                     b.el.hide();
30840
30841                 }, this);
30842
30843                 return;
30844             }
30845             
30846             var mx = 0;
30847             
30848             Roo.each(box, function(b){
30849                 
30850                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30851                 b.el.show();
30852
30853                 mx = Math.max(mx, b.x);
30854                 
30855             }, this);
30856             
30857             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30858             
30859             if(maxX < minX){
30860                 
30861                 Roo.each(box, function(b){
30862                 
30863                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30864                     b.el.hide();
30865                     
30866                 }, this);
30867                 
30868                 hit_end = true;
30869                 
30870                 return;
30871             }
30872             
30873             prune.push(box);
30874             
30875         }, this);
30876         
30877         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30878     },
30879     
30880     /** Sets position of item in DOM
30881     * @param {Element} item
30882     * @param {Number} x - horizontal position
30883     * @param {Number} y - vertical position
30884     * @param {Boolean} isInstant - disables transitions
30885     */
30886     _processVerticalLayoutQueue : function( queue, isInstant )
30887     {
30888         var pos = this.el.getBox(true);
30889         var x = pos.x;
30890         var y = pos.y;
30891         var maxY = [];
30892         
30893         for (var i = 0; i < this.cols; i++){
30894             maxY[i] = pos.y;
30895         }
30896         
30897         Roo.each(queue, function(box, k){
30898             
30899             var col = k % this.cols;
30900             
30901             Roo.each(box, function(b,kk){
30902                 
30903                 b.el.position('absolute');
30904                 
30905                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30906                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30907                 
30908                 if(b.size == 'md-left' || b.size == 'md-right'){
30909                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30910                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30911                 }
30912                 
30913                 b.el.setWidth(width);
30914                 b.el.setHeight(height);
30915                 // iframe?
30916                 b.el.select('iframe',true).setSize(width,height);
30917                 
30918             }, this);
30919             
30920             for (var i = 0; i < this.cols; i++){
30921                 
30922                 if(maxY[i] < maxY[col]){
30923                     col = i;
30924                     continue;
30925                 }
30926                 
30927                 col = Math.min(col, i);
30928                 
30929             }
30930             
30931             x = pos.x + col * (this.colWidth + this.padWidth);
30932             
30933             y = maxY[col];
30934             
30935             var positions = [];
30936             
30937             switch (box.length){
30938                 case 1 :
30939                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30940                     break;
30941                 case 2 :
30942                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30943                     break;
30944                 case 3 :
30945                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30946                     break;
30947                 case 4 :
30948                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30949                     break;
30950                 default :
30951                     break;
30952             }
30953             
30954             Roo.each(box, function(b,kk){
30955                 
30956                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30957                 
30958                 var sz = b.el.getSize();
30959                 
30960                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30961                 
30962             }, this);
30963             
30964         }, this);
30965         
30966         var mY = 0;
30967         
30968         for (var i = 0; i < this.cols; i++){
30969             mY = Math.max(mY, maxY[i]);
30970         }
30971         
30972         this.el.setHeight(mY - pos.y);
30973         
30974     },
30975     
30976 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30977 //    {
30978 //        var pos = this.el.getBox(true);
30979 //        var x = pos.x;
30980 //        var y = pos.y;
30981 //        var maxX = pos.right;
30982 //        
30983 //        var maxHeight = 0;
30984 //        
30985 //        Roo.each(items, function(item, k){
30986 //            
30987 //            var c = k % 2;
30988 //            
30989 //            item.el.position('absolute');
30990 //                
30991 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30992 //
30993 //            item.el.setWidth(width);
30994 //
30995 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30996 //
30997 //            item.el.setHeight(height);
30998 //            
30999 //            if(c == 0){
31000 //                item.el.setXY([x, y], isInstant ? false : true);
31001 //            } else {
31002 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31003 //            }
31004 //            
31005 //            y = y + height + this.alternativePadWidth;
31006 //            
31007 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31008 //            
31009 //        }, this);
31010 //        
31011 //        this.el.setHeight(maxHeight);
31012 //        
31013 //    },
31014     
31015     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31016     {
31017         var pos = this.el.getBox(true);
31018         
31019         var minX = pos.x;
31020         var minY = pos.y;
31021         
31022         var maxX = pos.right;
31023         
31024         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31025         
31026         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31027         
31028         Roo.each(queue, function(box, k){
31029             
31030             Roo.each(box, function(b, kk){
31031                 
31032                 b.el.position('absolute');
31033                 
31034                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31035                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31036                 
31037                 if(b.size == 'md-left' || b.size == 'md-right'){
31038                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31039                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31040                 }
31041                 
31042                 b.el.setWidth(width);
31043                 b.el.setHeight(height);
31044                 
31045             }, this);
31046             
31047             if(!box.length){
31048                 return;
31049             }
31050             
31051             var positions = [];
31052             
31053             switch (box.length){
31054                 case 1 :
31055                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31056                     break;
31057                 case 2 :
31058                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31059                     break;
31060                 case 3 :
31061                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31062                     break;
31063                 case 4 :
31064                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31065                     break;
31066                 default :
31067                     break;
31068             }
31069             
31070             Roo.each(box, function(b,kk){
31071                 
31072                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31073                 
31074                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31075                 
31076             }, this);
31077             
31078         }, this);
31079         
31080     },
31081     
31082     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31083     {
31084         Roo.each(eItems, function(b,k){
31085             
31086             b.size = (k == 0) ? 'sm' : 'xs';
31087             b.x = (k == 0) ? 2 : 1;
31088             b.y = (k == 0) ? 2 : 1;
31089             
31090             b.el.position('absolute');
31091             
31092             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31093                 
31094             b.el.setWidth(width);
31095             
31096             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31097             
31098             b.el.setHeight(height);
31099             
31100         }, this);
31101
31102         var positions = [];
31103         
31104         positions.push({
31105             x : maxX - this.unitWidth * 2 - this.gutter,
31106             y : minY
31107         });
31108         
31109         positions.push({
31110             x : maxX - this.unitWidth,
31111             y : minY + (this.unitWidth + this.gutter) * 2
31112         });
31113         
31114         positions.push({
31115             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31116             y : minY
31117         });
31118         
31119         Roo.each(eItems, function(b,k){
31120             
31121             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31122
31123         }, this);
31124         
31125     },
31126     
31127     getVerticalOneBoxColPositions : function(x, y, box)
31128     {
31129         var pos = [];
31130         
31131         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31132         
31133         if(box[0].size == 'md-left'){
31134             rand = 0;
31135         }
31136         
31137         if(box[0].size == 'md-right'){
31138             rand = 1;
31139         }
31140         
31141         pos.push({
31142             x : x + (this.unitWidth + this.gutter) * rand,
31143             y : y
31144         });
31145         
31146         return pos;
31147     },
31148     
31149     getVerticalTwoBoxColPositions : function(x, y, box)
31150     {
31151         var pos = [];
31152         
31153         if(box[0].size == 'xs'){
31154             
31155             pos.push({
31156                 x : x,
31157                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31158             });
31159
31160             pos.push({
31161                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31162                 y : y
31163             });
31164             
31165             return pos;
31166             
31167         }
31168         
31169         pos.push({
31170             x : x,
31171             y : y
31172         });
31173
31174         pos.push({
31175             x : x + (this.unitWidth + this.gutter) * 2,
31176             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31177         });
31178         
31179         return pos;
31180         
31181     },
31182     
31183     getVerticalThreeBoxColPositions : function(x, y, box)
31184     {
31185         var pos = [];
31186         
31187         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31188             
31189             pos.push({
31190                 x : x,
31191                 y : y
31192             });
31193
31194             pos.push({
31195                 x : x + (this.unitWidth + this.gutter) * 1,
31196                 y : y
31197             });
31198             
31199             pos.push({
31200                 x : x + (this.unitWidth + this.gutter) * 2,
31201                 y : y
31202             });
31203             
31204             return pos;
31205             
31206         }
31207         
31208         if(box[0].size == 'xs' && box[1].size == 'xs'){
31209             
31210             pos.push({
31211                 x : x,
31212                 y : y
31213             });
31214
31215             pos.push({
31216                 x : x,
31217                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31218             });
31219             
31220             pos.push({
31221                 x : x + (this.unitWidth + this.gutter) * 1,
31222                 y : y
31223             });
31224             
31225             return pos;
31226             
31227         }
31228         
31229         pos.push({
31230             x : x,
31231             y : y
31232         });
31233
31234         pos.push({
31235             x : x + (this.unitWidth + this.gutter) * 2,
31236             y : y
31237         });
31238
31239         pos.push({
31240             x : x + (this.unitWidth + this.gutter) * 2,
31241             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31242         });
31243             
31244         return pos;
31245         
31246     },
31247     
31248     getVerticalFourBoxColPositions : function(x, y, box)
31249     {
31250         var pos = [];
31251         
31252         if(box[0].size == 'xs'){
31253             
31254             pos.push({
31255                 x : x,
31256                 y : y
31257             });
31258
31259             pos.push({
31260                 x : x,
31261                 y : y + (this.unitHeight + this.gutter) * 1
31262             });
31263             
31264             pos.push({
31265                 x : x,
31266                 y : y + (this.unitHeight + this.gutter) * 2
31267             });
31268             
31269             pos.push({
31270                 x : x + (this.unitWidth + this.gutter) * 1,
31271                 y : y
31272             });
31273             
31274             return pos;
31275             
31276         }
31277         
31278         pos.push({
31279             x : x,
31280             y : y
31281         });
31282
31283         pos.push({
31284             x : x + (this.unitWidth + this.gutter) * 2,
31285             y : y
31286         });
31287
31288         pos.push({
31289             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31290             y : y + (this.unitHeight + this.gutter) * 1
31291         });
31292
31293         pos.push({
31294             x : x + (this.unitWidth + this.gutter) * 2,
31295             y : y + (this.unitWidth + this.gutter) * 2
31296         });
31297
31298         return pos;
31299         
31300     },
31301     
31302     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31303     {
31304         var pos = [];
31305         
31306         if(box[0].size == 'md-left'){
31307             pos.push({
31308                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31309                 y : minY
31310             });
31311             
31312             return pos;
31313         }
31314         
31315         if(box[0].size == 'md-right'){
31316             pos.push({
31317                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31318                 y : minY + (this.unitWidth + this.gutter) * 1
31319             });
31320             
31321             return pos;
31322         }
31323         
31324         var rand = Math.floor(Math.random() * (4 - box[0].y));
31325         
31326         pos.push({
31327             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31328             y : minY + (this.unitWidth + this.gutter) * rand
31329         });
31330         
31331         return pos;
31332         
31333     },
31334     
31335     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31336     {
31337         var pos = [];
31338         
31339         if(box[0].size == 'xs'){
31340             
31341             pos.push({
31342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31343                 y : minY
31344             });
31345
31346             pos.push({
31347                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31348                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31349             });
31350             
31351             return pos;
31352             
31353         }
31354         
31355         pos.push({
31356             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31357             y : minY
31358         });
31359
31360         pos.push({
31361             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31362             y : minY + (this.unitWidth + this.gutter) * 2
31363         });
31364         
31365         return pos;
31366         
31367     },
31368     
31369     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31370     {
31371         var pos = [];
31372         
31373         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31374             
31375             pos.push({
31376                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31377                 y : minY
31378             });
31379
31380             pos.push({
31381                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31382                 y : minY + (this.unitWidth + this.gutter) * 1
31383             });
31384             
31385             pos.push({
31386                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31387                 y : minY + (this.unitWidth + this.gutter) * 2
31388             });
31389             
31390             return pos;
31391             
31392         }
31393         
31394         if(box[0].size == 'xs' && box[1].size == 'xs'){
31395             
31396             pos.push({
31397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31398                 y : minY
31399             });
31400
31401             pos.push({
31402                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31403                 y : minY
31404             });
31405             
31406             pos.push({
31407                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31408                 y : minY + (this.unitWidth + this.gutter) * 1
31409             });
31410             
31411             return pos;
31412             
31413         }
31414         
31415         pos.push({
31416             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31417             y : minY
31418         });
31419
31420         pos.push({
31421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31422             y : minY + (this.unitWidth + this.gutter) * 2
31423         });
31424
31425         pos.push({
31426             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31427             y : minY + (this.unitWidth + this.gutter) * 2
31428         });
31429             
31430         return pos;
31431         
31432     },
31433     
31434     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31435     {
31436         var pos = [];
31437         
31438         if(box[0].size == 'xs'){
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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31447                 y : minY
31448             });
31449             
31450             pos.push({
31451                 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),
31452                 y : minY
31453             });
31454             
31455             pos.push({
31456                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31457                 y : minY + (this.unitWidth + this.gutter) * 1
31458             });
31459             
31460             return pos;
31461             
31462         }
31463         
31464         pos.push({
31465             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31466             y : minY
31467         });
31468         
31469         pos.push({
31470             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31471             y : minY + (this.unitWidth + this.gutter) * 2
31472         });
31473         
31474         pos.push({
31475             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31476             y : minY + (this.unitWidth + this.gutter) * 2
31477         });
31478         
31479         pos.push({
31480             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),
31481             y : minY + (this.unitWidth + this.gutter) * 2
31482         });
31483
31484         return pos;
31485         
31486     },
31487     
31488     /**
31489     * remove a Masonry Brick
31490     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31491     */
31492     removeBrick : function(brick_id)
31493     {
31494         if (!brick_id) {
31495             return;
31496         }
31497         
31498         for (var i = 0; i<this.bricks.length; i++) {
31499             if (this.bricks[i].id == brick_id) {
31500                 this.bricks.splice(i,1);
31501                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31502                 this.initial();
31503             }
31504         }
31505     },
31506     
31507     /**
31508     * adds a Masonry Brick
31509     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31510     */
31511     addBrick : function(cfg)
31512     {
31513         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31514         //this.register(cn);
31515         cn.parentId = this.id;
31516         cn.onRender(this.el, null);
31517         return cn;
31518     },
31519     
31520     /**
31521     * register a Masonry Brick
31522     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31523     */
31524     
31525     register : function(brick)
31526     {
31527         this.bricks.push(brick);
31528         brick.masonryId = this.id;
31529     },
31530     
31531     /**
31532     * clear all the Masonry Brick
31533     */
31534     clearAll : function()
31535     {
31536         this.bricks = [];
31537         //this.getChildContainer().dom.innerHTML = "";
31538         this.el.dom.innerHTML = '';
31539     },
31540     
31541     getSelected : function()
31542     {
31543         if (!this.selectedBrick) {
31544             return false;
31545         }
31546         
31547         return this.selectedBrick;
31548     }
31549 });
31550
31551 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31552     
31553     groups: {},
31554      /**
31555     * register a Masonry Layout
31556     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31557     */
31558     
31559     register : function(layout)
31560     {
31561         this.groups[layout.id] = layout;
31562     },
31563     /**
31564     * fetch a  Masonry Layout based on the masonry layout ID
31565     * @param {string} the masonry layout to add
31566     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31567     */
31568     
31569     get: function(layout_id) {
31570         if (typeof(this.groups[layout_id]) == 'undefined') {
31571             return false;
31572         }
31573         return this.groups[layout_id] ;
31574     }
31575     
31576     
31577     
31578 });
31579
31580  
31581
31582  /**
31583  *
31584  * This is based on 
31585  * http://masonry.desandro.com
31586  *
31587  * The idea is to render all the bricks based on vertical width...
31588  *
31589  * The original code extends 'outlayer' - we might need to use that....
31590  * 
31591  */
31592
31593
31594 /**
31595  * @class Roo.bootstrap.LayoutMasonryAuto
31596  * @extends Roo.bootstrap.Component
31597  * Bootstrap Layout Masonry class
31598  * 
31599  * @constructor
31600  * Create a new Element
31601  * @param {Object} config The config object
31602  */
31603
31604 Roo.bootstrap.LayoutMasonryAuto = function(config){
31605     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31606 };
31607
31608 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31609     
31610       /**
31611      * @cfg {Boolean} isFitWidth  - resize the width..
31612      */   
31613     isFitWidth : false,  // options..
31614     /**
31615      * @cfg {Boolean} isOriginLeft = left align?
31616      */   
31617     isOriginLeft : true,
31618     /**
31619      * @cfg {Boolean} isOriginTop = top align?
31620      */   
31621     isOriginTop : false,
31622     /**
31623      * @cfg {Boolean} isLayoutInstant = no animation?
31624      */   
31625     isLayoutInstant : false, // needed?
31626     /**
31627      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31628      */   
31629     isResizingContainer : true,
31630     /**
31631      * @cfg {Number} columnWidth  width of the columns 
31632      */   
31633     
31634     columnWidth : 0,
31635     
31636     /**
31637      * @cfg {Number} maxCols maximum number of columns
31638      */   
31639     
31640     maxCols: 0,
31641     /**
31642      * @cfg {Number} padHeight padding below box..
31643      */   
31644     
31645     padHeight : 10, 
31646     
31647     /**
31648      * @cfg {Boolean} isAutoInitial defalut true
31649      */   
31650     
31651     isAutoInitial : true, 
31652     
31653     // private?
31654     gutter : 0,
31655     
31656     containerWidth: 0,
31657     initialColumnWidth : 0,
31658     currentSize : null,
31659     
31660     colYs : null, // array.
31661     maxY : 0,
31662     padWidth: 10,
31663     
31664     
31665     tag: 'div',
31666     cls: '',
31667     bricks: null, //CompositeElement
31668     cols : 0, // array?
31669     // element : null, // wrapped now this.el
31670     _isLayoutInited : null, 
31671     
31672     
31673     getAutoCreate : function(){
31674         
31675         var cfg = {
31676             tag: this.tag,
31677             cls: 'blog-masonary-wrapper ' + this.cls,
31678             cn : {
31679                 cls : 'mas-boxes masonary'
31680             }
31681         };
31682         
31683         return cfg;
31684     },
31685     
31686     getChildContainer: function( )
31687     {
31688         if (this.boxesEl) {
31689             return this.boxesEl;
31690         }
31691         
31692         this.boxesEl = this.el.select('.mas-boxes').first();
31693         
31694         return this.boxesEl;
31695     },
31696     
31697     
31698     initEvents : function()
31699     {
31700         var _this = this;
31701         
31702         if(this.isAutoInitial){
31703             Roo.log('hook children rendered');
31704             this.on('childrenrendered', function() {
31705                 Roo.log('children rendered');
31706                 _this.initial();
31707             } ,this);
31708         }
31709         
31710     },
31711     
31712     initial : function()
31713     {
31714         this.reloadItems();
31715
31716         this.currentSize = this.el.getBox(true);
31717
31718         /// was window resize... - let's see if this works..
31719         Roo.EventManager.onWindowResize(this.resize, this); 
31720
31721         if(!this.isAutoInitial){
31722             this.layout();
31723             return;
31724         }
31725         
31726         this.layout.defer(500,this);
31727     },
31728     
31729     reloadItems: function()
31730     {
31731         this.bricks = this.el.select('.masonry-brick', true);
31732         
31733         this.bricks.each(function(b) {
31734             //Roo.log(b.getSize());
31735             if (!b.attr('originalwidth')) {
31736                 b.attr('originalwidth',  b.getSize().width);
31737             }
31738             
31739         });
31740         
31741         Roo.log(this.bricks.elements.length);
31742     },
31743     
31744     resize : function()
31745     {
31746         Roo.log('resize');
31747         var cs = this.el.getBox(true);
31748         
31749         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31750             Roo.log("no change in with or X");
31751             return;
31752         }
31753         this.currentSize = cs;
31754         this.layout();
31755     },
31756     
31757     layout : function()
31758     {
31759          Roo.log('layout');
31760         this._resetLayout();
31761         //this._manageStamps();
31762       
31763         // don't animate first layout
31764         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31765         this.layoutItems( isInstant );
31766       
31767         // flag for initalized
31768         this._isLayoutInited = true;
31769     },
31770     
31771     layoutItems : function( isInstant )
31772     {
31773         //var items = this._getItemsForLayout( this.items );
31774         // original code supports filtering layout items.. we just ignore it..
31775         
31776         this._layoutItems( this.bricks , isInstant );
31777       
31778         this._postLayout();
31779     },
31780     _layoutItems : function ( items , isInstant)
31781     {
31782        //this.fireEvent( 'layout', this, items );
31783     
31784
31785         if ( !items || !items.elements.length ) {
31786           // no items, emit event with empty array
31787             return;
31788         }
31789
31790         var queue = [];
31791         items.each(function(item) {
31792             Roo.log("layout item");
31793             Roo.log(item);
31794             // get x/y object from method
31795             var position = this._getItemLayoutPosition( item );
31796             // enqueue
31797             position.item = item;
31798             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31799             queue.push( position );
31800         }, this);
31801       
31802         this._processLayoutQueue( queue );
31803     },
31804     /** Sets position of item in DOM
31805     * @param {Element} item
31806     * @param {Number} x - horizontal position
31807     * @param {Number} y - vertical position
31808     * @param {Boolean} isInstant - disables transitions
31809     */
31810     _processLayoutQueue : function( queue )
31811     {
31812         for ( var i=0, len = queue.length; i < len; i++ ) {
31813             var obj = queue[i];
31814             obj.item.position('absolute');
31815             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31816         }
31817     },
31818       
31819     
31820     /**
31821     * Any logic you want to do after each layout,
31822     * i.e. size the container
31823     */
31824     _postLayout : function()
31825     {
31826         this.resizeContainer();
31827     },
31828     
31829     resizeContainer : function()
31830     {
31831         if ( !this.isResizingContainer ) {
31832             return;
31833         }
31834         var size = this._getContainerSize();
31835         if ( size ) {
31836             this.el.setSize(size.width,size.height);
31837             this.boxesEl.setSize(size.width,size.height);
31838         }
31839     },
31840     
31841     
31842     
31843     _resetLayout : function()
31844     {
31845         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31846         this.colWidth = this.el.getWidth();
31847         //this.gutter = this.el.getWidth(); 
31848         
31849         this.measureColumns();
31850
31851         // reset column Y
31852         var i = this.cols;
31853         this.colYs = [];
31854         while (i--) {
31855             this.colYs.push( 0 );
31856         }
31857     
31858         this.maxY = 0;
31859     },
31860
31861     measureColumns : function()
31862     {
31863         this.getContainerWidth();
31864       // if columnWidth is 0, default to outerWidth of first item
31865         if ( !this.columnWidth ) {
31866             var firstItem = this.bricks.first();
31867             Roo.log(firstItem);
31868             this.columnWidth  = this.containerWidth;
31869             if (firstItem && firstItem.attr('originalwidth') ) {
31870                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31871             }
31872             // columnWidth fall back to item of first element
31873             Roo.log("set column width?");
31874                         this.initialColumnWidth = this.columnWidth  ;
31875
31876             // if first elem has no width, default to size of container
31877             
31878         }
31879         
31880         
31881         if (this.initialColumnWidth) {
31882             this.columnWidth = this.initialColumnWidth;
31883         }
31884         
31885         
31886             
31887         // column width is fixed at the top - however if container width get's smaller we should
31888         // reduce it...
31889         
31890         // this bit calcs how man columns..
31891             
31892         var columnWidth = this.columnWidth += this.gutter;
31893       
31894         // calculate columns
31895         var containerWidth = this.containerWidth + this.gutter;
31896         
31897         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31898         // fix rounding errors, typically with gutters
31899         var excess = columnWidth - containerWidth % columnWidth;
31900         
31901         
31902         // if overshoot is less than a pixel, round up, otherwise floor it
31903         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31904         cols = Math[ mathMethod ]( cols );
31905         this.cols = Math.max( cols, 1 );
31906         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31907         
31908          // padding positioning..
31909         var totalColWidth = this.cols * this.columnWidth;
31910         var padavail = this.containerWidth - totalColWidth;
31911         // so for 2 columns - we need 3 'pads'
31912         
31913         var padNeeded = (1+this.cols) * this.padWidth;
31914         
31915         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31916         
31917         this.columnWidth += padExtra
31918         //this.padWidth = Math.floor(padavail /  ( this.cols));
31919         
31920         // adjust colum width so that padding is fixed??
31921         
31922         // we have 3 columns ... total = width * 3
31923         // we have X left over... that should be used by 
31924         
31925         //if (this.expandC) {
31926             
31927         //}
31928         
31929         
31930         
31931     },
31932     
31933     getContainerWidth : function()
31934     {
31935        /* // container is parent if fit width
31936         var container = this.isFitWidth ? this.element.parentNode : this.element;
31937         // check that this.size and size are there
31938         // IE8 triggers resize on body size change, so they might not be
31939         
31940         var size = getSize( container );  //FIXME
31941         this.containerWidth = size && size.innerWidth; //FIXME
31942         */
31943          
31944         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31945         
31946     },
31947     
31948     _getItemLayoutPosition : function( item )  // what is item?
31949     {
31950         // we resize the item to our columnWidth..
31951       
31952         item.setWidth(this.columnWidth);
31953         item.autoBoxAdjust  = false;
31954         
31955         var sz = item.getSize();
31956  
31957         // how many columns does this brick span
31958         var remainder = this.containerWidth % this.columnWidth;
31959         
31960         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31961         // round if off by 1 pixel, otherwise use ceil
31962         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31963         colSpan = Math.min( colSpan, this.cols );
31964         
31965         // normally this should be '1' as we dont' currently allow multi width columns..
31966         
31967         var colGroup = this._getColGroup( colSpan );
31968         // get the minimum Y value from the columns
31969         var minimumY = Math.min.apply( Math, colGroup );
31970         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31971         
31972         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31973          
31974         // position the brick
31975         var position = {
31976             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31977             y: this.currentSize.y + minimumY + this.padHeight
31978         };
31979         
31980         Roo.log(position);
31981         // apply setHeight to necessary columns
31982         var setHeight = minimumY + sz.height + this.padHeight;
31983         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31984         
31985         var setSpan = this.cols + 1 - colGroup.length;
31986         for ( var i = 0; i < setSpan; i++ ) {
31987           this.colYs[ shortColIndex + i ] = setHeight ;
31988         }
31989       
31990         return position;
31991     },
31992     
31993     /**
31994      * @param {Number} colSpan - number of columns the element spans
31995      * @returns {Array} colGroup
31996      */
31997     _getColGroup : function( colSpan )
31998     {
31999         if ( colSpan < 2 ) {
32000           // if brick spans only one column, use all the column Ys
32001           return this.colYs;
32002         }
32003       
32004         var colGroup = [];
32005         // how many different places could this brick fit horizontally
32006         var groupCount = this.cols + 1 - colSpan;
32007         // for each group potential horizontal position
32008         for ( var i = 0; i < groupCount; i++ ) {
32009           // make an array of colY values for that one group
32010           var groupColYs = this.colYs.slice( i, i + colSpan );
32011           // and get the max value of the array
32012           colGroup[i] = Math.max.apply( Math, groupColYs );
32013         }
32014         return colGroup;
32015     },
32016     /*
32017     _manageStamp : function( stamp )
32018     {
32019         var stampSize =  stamp.getSize();
32020         var offset = stamp.getBox();
32021         // get the columns that this stamp affects
32022         var firstX = this.isOriginLeft ? offset.x : offset.right;
32023         var lastX = firstX + stampSize.width;
32024         var firstCol = Math.floor( firstX / this.columnWidth );
32025         firstCol = Math.max( 0, firstCol );
32026         
32027         var lastCol = Math.floor( lastX / this.columnWidth );
32028         // lastCol should not go over if multiple of columnWidth #425
32029         lastCol -= lastX % this.columnWidth ? 0 : 1;
32030         lastCol = Math.min( this.cols - 1, lastCol );
32031         
32032         // set colYs to bottom of the stamp
32033         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32034             stampSize.height;
32035             
32036         for ( var i = firstCol; i <= lastCol; i++ ) {
32037           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32038         }
32039     },
32040     */
32041     
32042     _getContainerSize : function()
32043     {
32044         this.maxY = Math.max.apply( Math, this.colYs );
32045         var size = {
32046             height: this.maxY
32047         };
32048       
32049         if ( this.isFitWidth ) {
32050             size.width = this._getContainerFitWidth();
32051         }
32052       
32053         return size;
32054     },
32055     
32056     _getContainerFitWidth : function()
32057     {
32058         var unusedCols = 0;
32059         // count unused columns
32060         var i = this.cols;
32061         while ( --i ) {
32062           if ( this.colYs[i] !== 0 ) {
32063             break;
32064           }
32065           unusedCols++;
32066         }
32067         // fit container to columns that have been used
32068         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32069     },
32070     
32071     needsResizeLayout : function()
32072     {
32073         var previousWidth = this.containerWidth;
32074         this.getContainerWidth();
32075         return previousWidth !== this.containerWidth;
32076     }
32077  
32078 });
32079
32080  
32081
32082  /*
32083  * - LGPL
32084  *
32085  * element
32086  * 
32087  */
32088
32089 /**
32090  * @class Roo.bootstrap.MasonryBrick
32091  * @extends Roo.bootstrap.Component
32092  * Bootstrap MasonryBrick class
32093  * 
32094  * @constructor
32095  * Create a new MasonryBrick
32096  * @param {Object} config The config object
32097  */
32098
32099 Roo.bootstrap.MasonryBrick = function(config){
32100     
32101     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32102     
32103     Roo.bootstrap.MasonryBrick.register(this);
32104     
32105     this.addEvents({
32106         // raw events
32107         /**
32108          * @event click
32109          * When a MasonryBrick is clcik
32110          * @param {Roo.bootstrap.MasonryBrick} this
32111          * @param {Roo.EventObject} e
32112          */
32113         "click" : true
32114     });
32115 };
32116
32117 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32118     
32119     /**
32120      * @cfg {String} title
32121      */   
32122     title : '',
32123     /**
32124      * @cfg {String} html
32125      */   
32126     html : '',
32127     /**
32128      * @cfg {String} bgimage
32129      */   
32130     bgimage : '',
32131     /**
32132      * @cfg {String} videourl
32133      */   
32134     videourl : '',
32135     /**
32136      * @cfg {String} cls
32137      */   
32138     cls : '',
32139     /**
32140      * @cfg {String} href
32141      */   
32142     href : '',
32143     /**
32144      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32145      */   
32146     size : 'xs',
32147     
32148     /**
32149      * @cfg {String} placetitle (center|bottom)
32150      */   
32151     placetitle : '',
32152     
32153     /**
32154      * @cfg {Boolean} isFitContainer defalut true
32155      */   
32156     isFitContainer : true, 
32157     
32158     /**
32159      * @cfg {Boolean} preventDefault defalut false
32160      */   
32161     preventDefault : false, 
32162     
32163     /**
32164      * @cfg {Boolean} inverse defalut false
32165      */   
32166     maskInverse : false, 
32167     
32168     getAutoCreate : function()
32169     {
32170         if(!this.isFitContainer){
32171             return this.getSplitAutoCreate();
32172         }
32173         
32174         var cls = 'masonry-brick masonry-brick-full';
32175         
32176         if(this.href.length){
32177             cls += ' masonry-brick-link';
32178         }
32179         
32180         if(this.bgimage.length){
32181             cls += ' masonry-brick-image';
32182         }
32183         
32184         if(this.maskInverse){
32185             cls += ' mask-inverse';
32186         }
32187         
32188         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32189             cls += ' enable-mask';
32190         }
32191         
32192         if(this.size){
32193             cls += ' masonry-' + this.size + '-brick';
32194         }
32195         
32196         if(this.placetitle.length){
32197             
32198             switch (this.placetitle) {
32199                 case 'center' :
32200                     cls += ' masonry-center-title';
32201                     break;
32202                 case 'bottom' :
32203                     cls += ' masonry-bottom-title';
32204                     break;
32205                 default:
32206                     break;
32207             }
32208             
32209         } else {
32210             if(!this.html.length && !this.bgimage.length){
32211                 cls += ' masonry-center-title';
32212             }
32213
32214             if(!this.html.length && this.bgimage.length){
32215                 cls += ' masonry-bottom-title';
32216             }
32217         }
32218         
32219         if(this.cls){
32220             cls += ' ' + this.cls;
32221         }
32222         
32223         var cfg = {
32224             tag: (this.href.length) ? 'a' : 'div',
32225             cls: cls,
32226             cn: [
32227                 {
32228                     tag: 'div',
32229                     cls: 'masonry-brick-mask'
32230                 },
32231                 {
32232                     tag: 'div',
32233                     cls: 'masonry-brick-paragraph',
32234                     cn: []
32235                 }
32236             ]
32237         };
32238         
32239         if(this.href.length){
32240             cfg.href = this.href;
32241         }
32242         
32243         var cn = cfg.cn[1].cn;
32244         
32245         if(this.title.length){
32246             cn.push({
32247                 tag: 'h4',
32248                 cls: 'masonry-brick-title',
32249                 html: this.title
32250             });
32251         }
32252         
32253         if(this.html.length){
32254             cn.push({
32255                 tag: 'p',
32256                 cls: 'masonry-brick-text',
32257                 html: this.html
32258             });
32259         }
32260         
32261         if (!this.title.length && !this.html.length) {
32262             cfg.cn[1].cls += ' hide';
32263         }
32264         
32265         if(this.bgimage.length){
32266             cfg.cn.push({
32267                 tag: 'img',
32268                 cls: 'masonry-brick-image-view',
32269                 src: this.bgimage
32270             });
32271         }
32272         
32273         if(this.videourl.length){
32274             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32275             // youtube support only?
32276             cfg.cn.push({
32277                 tag: 'iframe',
32278                 cls: 'masonry-brick-image-view',
32279                 src: vurl,
32280                 frameborder : 0,
32281                 allowfullscreen : true
32282             });
32283         }
32284         
32285         return cfg;
32286         
32287     },
32288     
32289     getSplitAutoCreate : function()
32290     {
32291         var cls = 'masonry-brick masonry-brick-split';
32292         
32293         if(this.href.length){
32294             cls += ' masonry-brick-link';
32295         }
32296         
32297         if(this.bgimage.length){
32298             cls += ' masonry-brick-image';
32299         }
32300         
32301         if(this.size){
32302             cls += ' masonry-' + this.size + '-brick';
32303         }
32304         
32305         switch (this.placetitle) {
32306             case 'center' :
32307                 cls += ' masonry-center-title';
32308                 break;
32309             case 'bottom' :
32310                 cls += ' masonry-bottom-title';
32311                 break;
32312             default:
32313                 if(!this.bgimage.length){
32314                     cls += ' masonry-center-title';
32315                 }
32316
32317                 if(this.bgimage.length){
32318                     cls += ' masonry-bottom-title';
32319                 }
32320                 break;
32321         }
32322         
32323         if(this.cls){
32324             cls += ' ' + this.cls;
32325         }
32326         
32327         var cfg = {
32328             tag: (this.href.length) ? 'a' : 'div',
32329             cls: cls,
32330             cn: [
32331                 {
32332                     tag: 'div',
32333                     cls: 'masonry-brick-split-head',
32334                     cn: [
32335                         {
32336                             tag: 'div',
32337                             cls: 'masonry-brick-paragraph',
32338                             cn: []
32339                         }
32340                     ]
32341                 },
32342                 {
32343                     tag: 'div',
32344                     cls: 'masonry-brick-split-body',
32345                     cn: []
32346                 }
32347             ]
32348         };
32349         
32350         if(this.href.length){
32351             cfg.href = this.href;
32352         }
32353         
32354         if(this.title.length){
32355             cfg.cn[0].cn[0].cn.push({
32356                 tag: 'h4',
32357                 cls: 'masonry-brick-title',
32358                 html: this.title
32359             });
32360         }
32361         
32362         if(this.html.length){
32363             cfg.cn[1].cn.push({
32364                 tag: 'p',
32365                 cls: 'masonry-brick-text',
32366                 html: this.html
32367             });
32368         }
32369
32370         if(this.bgimage.length){
32371             cfg.cn[0].cn.push({
32372                 tag: 'img',
32373                 cls: 'masonry-brick-image-view',
32374                 src: this.bgimage
32375             });
32376         }
32377         
32378         if(this.videourl.length){
32379             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32380             // youtube support only?
32381             cfg.cn[0].cn.cn.push({
32382                 tag: 'iframe',
32383                 cls: 'masonry-brick-image-view',
32384                 src: vurl,
32385                 frameborder : 0,
32386                 allowfullscreen : true
32387             });
32388         }
32389         
32390         return cfg;
32391     },
32392     
32393     initEvents: function() 
32394     {
32395         switch (this.size) {
32396             case 'xs' :
32397                 this.x = 1;
32398                 this.y = 1;
32399                 break;
32400             case 'sm' :
32401                 this.x = 2;
32402                 this.y = 2;
32403                 break;
32404             case 'md' :
32405             case 'md-left' :
32406             case 'md-right' :
32407                 this.x = 3;
32408                 this.y = 3;
32409                 break;
32410             case 'tall' :
32411                 this.x = 2;
32412                 this.y = 3;
32413                 break;
32414             case 'wide' :
32415                 this.x = 3;
32416                 this.y = 2;
32417                 break;
32418             case 'wide-thin' :
32419                 this.x = 3;
32420                 this.y = 1;
32421                 break;
32422                         
32423             default :
32424                 break;
32425         }
32426         
32427         if(Roo.isTouch){
32428             this.el.on('touchstart', this.onTouchStart, this);
32429             this.el.on('touchmove', this.onTouchMove, this);
32430             this.el.on('touchend', this.onTouchEnd, this);
32431             this.el.on('contextmenu', this.onContextMenu, this);
32432         } else {
32433             this.el.on('mouseenter'  ,this.enter, this);
32434             this.el.on('mouseleave', this.leave, this);
32435             this.el.on('click', this.onClick, this);
32436         }
32437         
32438         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32439             this.parent().bricks.push(this);   
32440         }
32441         
32442     },
32443     
32444     onClick: function(e, el)
32445     {
32446         var time = this.endTimer - this.startTimer;
32447         // Roo.log(e.preventDefault());
32448         if(Roo.isTouch){
32449             if(time > 1000){
32450                 e.preventDefault();
32451                 return;
32452             }
32453         }
32454         
32455         if(!this.preventDefault){
32456             return;
32457         }
32458         
32459         e.preventDefault();
32460         
32461         if (this.activcClass != '') {
32462             this.selectBrick();
32463         }
32464         
32465         this.fireEvent('click', this);
32466     },
32467     
32468     enter: function(e, el)
32469     {
32470         e.preventDefault();
32471         
32472         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32473             return;
32474         }
32475         
32476         if(this.bgimage.length && this.html.length){
32477             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32478         }
32479     },
32480     
32481     leave: function(e, el)
32482     {
32483         e.preventDefault();
32484         
32485         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32486             return;
32487         }
32488         
32489         if(this.bgimage.length && this.html.length){
32490             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32491         }
32492     },
32493     
32494     onTouchStart: function(e, el)
32495     {
32496 //        e.preventDefault();
32497         
32498         this.touchmoved = false;
32499         
32500         if(!this.isFitContainer){
32501             return;
32502         }
32503         
32504         if(!this.bgimage.length || !this.html.length){
32505             return;
32506         }
32507         
32508         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32509         
32510         this.timer = new Date().getTime();
32511         
32512     },
32513     
32514     onTouchMove: function(e, el)
32515     {
32516         this.touchmoved = true;
32517     },
32518     
32519     onContextMenu : function(e,el)
32520     {
32521         e.preventDefault();
32522         e.stopPropagation();
32523         return false;
32524     },
32525     
32526     onTouchEnd: function(e, el)
32527     {
32528 //        e.preventDefault();
32529         
32530         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32531         
32532             this.leave(e,el);
32533             
32534             return;
32535         }
32536         
32537         if(!this.bgimage.length || !this.html.length){
32538             
32539             if(this.href.length){
32540                 window.location.href = this.href;
32541             }
32542             
32543             return;
32544         }
32545         
32546         if(!this.isFitContainer){
32547             return;
32548         }
32549         
32550         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32551         
32552         window.location.href = this.href;
32553     },
32554     
32555     //selection on single brick only
32556     selectBrick : function() {
32557         
32558         if (!this.parentId) {
32559             return;
32560         }
32561         
32562         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32563         var index = m.selectedBrick.indexOf(this.id);
32564         
32565         if ( index > -1) {
32566             m.selectedBrick.splice(index,1);
32567             this.el.removeClass(this.activeClass);
32568             return;
32569         }
32570         
32571         for(var i = 0; i < m.selectedBrick.length; i++) {
32572             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32573             b.el.removeClass(b.activeClass);
32574         }
32575         
32576         m.selectedBrick = [];
32577         
32578         m.selectedBrick.push(this.id);
32579         this.el.addClass(this.activeClass);
32580         return;
32581     }
32582     
32583 });
32584
32585 Roo.apply(Roo.bootstrap.MasonryBrick, {
32586     
32587     //groups: {},
32588     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32589      /**
32590     * register a Masonry Brick
32591     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32592     */
32593     
32594     register : function(brick)
32595     {
32596         //this.groups[brick.id] = brick;
32597         this.groups.add(brick.id, brick);
32598     },
32599     /**
32600     * fetch a  masonry brick based on the masonry brick ID
32601     * @param {string} the masonry brick to add
32602     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32603     */
32604     
32605     get: function(brick_id) 
32606     {
32607         // if (typeof(this.groups[brick_id]) == 'undefined') {
32608         //     return false;
32609         // }
32610         // return this.groups[brick_id] ;
32611         
32612         if(this.groups.key(brick_id)) {
32613             return this.groups.key(brick_id);
32614         }
32615         
32616         return false;
32617     }
32618     
32619     
32620     
32621 });
32622
32623  /*
32624  * - LGPL
32625  *
32626  * element
32627  * 
32628  */
32629
32630 /**
32631  * @class Roo.bootstrap.Brick
32632  * @extends Roo.bootstrap.Component
32633  * Bootstrap Brick class
32634  * 
32635  * @constructor
32636  * Create a new Brick
32637  * @param {Object} config The config object
32638  */
32639
32640 Roo.bootstrap.Brick = function(config){
32641     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32642     
32643     this.addEvents({
32644         // raw events
32645         /**
32646          * @event click
32647          * When a Brick is click
32648          * @param {Roo.bootstrap.Brick} this
32649          * @param {Roo.EventObject} e
32650          */
32651         "click" : true
32652     });
32653 };
32654
32655 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32656     
32657     /**
32658      * @cfg {String} title
32659      */   
32660     title : '',
32661     /**
32662      * @cfg {String} html
32663      */   
32664     html : '',
32665     /**
32666      * @cfg {String} bgimage
32667      */   
32668     bgimage : '',
32669     /**
32670      * @cfg {String} cls
32671      */   
32672     cls : '',
32673     /**
32674      * @cfg {String} href
32675      */   
32676     href : '',
32677     /**
32678      * @cfg {String} video
32679      */   
32680     video : '',
32681     /**
32682      * @cfg {Boolean} square
32683      */   
32684     square : true,
32685     
32686     getAutoCreate : function()
32687     {
32688         var cls = 'roo-brick';
32689         
32690         if(this.href.length){
32691             cls += ' roo-brick-link';
32692         }
32693         
32694         if(this.bgimage.length){
32695             cls += ' roo-brick-image';
32696         }
32697         
32698         if(!this.html.length && !this.bgimage.length){
32699             cls += ' roo-brick-center-title';
32700         }
32701         
32702         if(!this.html.length && this.bgimage.length){
32703             cls += ' roo-brick-bottom-title';
32704         }
32705         
32706         if(this.cls){
32707             cls += ' ' + this.cls;
32708         }
32709         
32710         var cfg = {
32711             tag: (this.href.length) ? 'a' : 'div',
32712             cls: cls,
32713             cn: [
32714                 {
32715                     tag: 'div',
32716                     cls: 'roo-brick-paragraph',
32717                     cn: []
32718                 }
32719             ]
32720         };
32721         
32722         if(this.href.length){
32723             cfg.href = this.href;
32724         }
32725         
32726         var cn = cfg.cn[0].cn;
32727         
32728         if(this.title.length){
32729             cn.push({
32730                 tag: 'h4',
32731                 cls: 'roo-brick-title',
32732                 html: this.title
32733             });
32734         }
32735         
32736         if(this.html.length){
32737             cn.push({
32738                 tag: 'p',
32739                 cls: 'roo-brick-text',
32740                 html: this.html
32741             });
32742         } else {
32743             cn.cls += ' hide';
32744         }
32745         
32746         if(this.bgimage.length){
32747             cfg.cn.push({
32748                 tag: 'img',
32749                 cls: 'roo-brick-image-view',
32750                 src: this.bgimage
32751             });
32752         }
32753         
32754         return cfg;
32755     },
32756     
32757     initEvents: function() 
32758     {
32759         if(this.title.length || this.html.length){
32760             this.el.on('mouseenter'  ,this.enter, this);
32761             this.el.on('mouseleave', this.leave, this);
32762         }
32763         
32764         Roo.EventManager.onWindowResize(this.resize, this); 
32765         
32766         if(this.bgimage.length){
32767             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32768             this.imageEl.on('load', this.onImageLoad, this);
32769             return;
32770         }
32771         
32772         this.resize();
32773     },
32774     
32775     onImageLoad : function()
32776     {
32777         this.resize();
32778     },
32779     
32780     resize : function()
32781     {
32782         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32783         
32784         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32785         
32786         if(this.bgimage.length){
32787             var image = this.el.select('.roo-brick-image-view', true).first();
32788             
32789             image.setWidth(paragraph.getWidth());
32790             
32791             if(this.square){
32792                 image.setHeight(paragraph.getWidth());
32793             }
32794             
32795             this.el.setHeight(image.getHeight());
32796             paragraph.setHeight(image.getHeight());
32797             
32798         }
32799         
32800     },
32801     
32802     enter: function(e, el)
32803     {
32804         e.preventDefault();
32805         
32806         if(this.bgimage.length){
32807             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32808             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32809         }
32810     },
32811     
32812     leave: function(e, el)
32813     {
32814         e.preventDefault();
32815         
32816         if(this.bgimage.length){
32817             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32818             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32819         }
32820     }
32821     
32822 });
32823
32824  
32825
32826  /*
32827  * - LGPL
32828  *
32829  * Input
32830  * 
32831  */
32832
32833 /**
32834  * @class Roo.bootstrap.NumberField
32835  * @extends Roo.bootstrap.Input
32836  * Bootstrap NumberField class
32837  * 
32838  * 
32839  * 
32840  * 
32841  * @constructor
32842  * Create a new NumberField
32843  * @param {Object} config The config object
32844  */
32845
32846 Roo.bootstrap.NumberField = function(config){
32847     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32848 };
32849
32850 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32851     
32852     /**
32853      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32854      */
32855     allowDecimals : true,
32856     /**
32857      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32858      */
32859     decimalSeparator : ".",
32860     /**
32861      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32862      */
32863     decimalPrecision : 2,
32864     /**
32865      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32866      */
32867     allowNegative : true,
32868     /**
32869      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32870      */
32871     minValue : Number.NEGATIVE_INFINITY,
32872     /**
32873      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32874      */
32875     maxValue : Number.MAX_VALUE,
32876     /**
32877      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32878      */
32879     minText : "The minimum value for this field is {0}",
32880     /**
32881      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32882      */
32883     maxText : "The maximum value for this field is {0}",
32884     /**
32885      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32886      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32887      */
32888     nanText : "{0} is not a valid number",
32889     /**
32890      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32891      */
32892     castInt : true,
32893
32894     // private
32895     initEvents : function()
32896     {   
32897         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32898         
32899         var allowed = "0123456789";
32900         
32901         if(this.allowDecimals){
32902             allowed += this.decimalSeparator;
32903         }
32904         
32905         if(this.allowNegative){
32906             allowed += "-";
32907         }
32908         
32909         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32910         
32911         var keyPress = function(e){
32912             
32913             var k = e.getKey();
32914             
32915             var c = e.getCharCode();
32916             
32917             if(
32918                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32919                     allowed.indexOf(String.fromCharCode(c)) === -1
32920             ){
32921                 e.stopEvent();
32922                 return;
32923             }
32924             
32925             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32926                 return;
32927             }
32928             
32929             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32930                 e.stopEvent();
32931             }
32932         };
32933         
32934         this.el.on("keypress", keyPress, this);
32935     },
32936     
32937     validateValue : function(value)
32938     {
32939         
32940         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32941             return false;
32942         }
32943         
32944         var num = this.parseValue(value);
32945         
32946         if(isNaN(num)){
32947             this.markInvalid(String.format(this.nanText, value));
32948             return false;
32949         }
32950         
32951         if(num < this.minValue){
32952             this.markInvalid(String.format(this.minText, this.minValue));
32953             return false;
32954         }
32955         
32956         if(num > this.maxValue){
32957             this.markInvalid(String.format(this.maxText, this.maxValue));
32958             return false;
32959         }
32960         
32961         return true;
32962     },
32963
32964     getValue : function()
32965     {
32966         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32967     },
32968
32969     parseValue : function(value)
32970     {
32971         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32972         return isNaN(value) ? '' : value;
32973     },
32974
32975     fixPrecision : function(value)
32976     {
32977         var nan = isNaN(value);
32978         
32979         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32980             return nan ? '' : value;
32981         }
32982         return parseFloat(value).toFixed(this.decimalPrecision);
32983     },
32984
32985     setValue : function(v)
32986     {
32987         v = this.fixPrecision(v);
32988         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32989     },
32990
32991     decimalPrecisionFcn : function(v)
32992     {
32993         return Math.floor(v);
32994     },
32995
32996     beforeBlur : function()
32997     {
32998         if(!this.castInt){
32999             return;
33000         }
33001         
33002         var v = this.parseValue(this.getRawValue());
33003         if(v){
33004             this.setValue(v);
33005         }
33006     }
33007     
33008 });
33009
33010  
33011
33012 /*
33013 * Licence: LGPL
33014 */
33015
33016 /**
33017  * @class Roo.bootstrap.DocumentSlider
33018  * @extends Roo.bootstrap.Component
33019  * Bootstrap DocumentSlider class
33020  * 
33021  * @constructor
33022  * Create a new DocumentViewer
33023  * @param {Object} config The config object
33024  */
33025
33026 Roo.bootstrap.DocumentSlider = function(config){
33027     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33028     
33029     this.files = [];
33030     
33031     this.addEvents({
33032         /**
33033          * @event initial
33034          * Fire after initEvent
33035          * @param {Roo.bootstrap.DocumentSlider} this
33036          */
33037         "initial" : true,
33038         /**
33039          * @event update
33040          * Fire after update
33041          * @param {Roo.bootstrap.DocumentSlider} this
33042          */
33043         "update" : true,
33044         /**
33045          * @event click
33046          * Fire after click
33047          * @param {Roo.bootstrap.DocumentSlider} this
33048          */
33049         "click" : true
33050     });
33051 };
33052
33053 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33054     
33055     files : false,
33056     
33057     indicator : 0,
33058     
33059     getAutoCreate : function()
33060     {
33061         var cfg = {
33062             tag : 'div',
33063             cls : 'roo-document-slider',
33064             cn : [
33065                 {
33066                     tag : 'div',
33067                     cls : 'roo-document-slider-header',
33068                     cn : [
33069                         {
33070                             tag : 'div',
33071                             cls : 'roo-document-slider-header-title'
33072                         }
33073                     ]
33074                 },
33075                 {
33076                     tag : 'div',
33077                     cls : 'roo-document-slider-body',
33078                     cn : [
33079                         {
33080                             tag : 'div',
33081                             cls : 'roo-document-slider-prev',
33082                             cn : [
33083                                 {
33084                                     tag : 'i',
33085                                     cls : 'fa fa-chevron-left'
33086                                 }
33087                             ]
33088                         },
33089                         {
33090                             tag : 'div',
33091                             cls : 'roo-document-slider-thumb',
33092                             cn : [
33093                                 {
33094                                     tag : 'img',
33095                                     cls : 'roo-document-slider-image'
33096                                 }
33097                             ]
33098                         },
33099                         {
33100                             tag : 'div',
33101                             cls : 'roo-document-slider-next',
33102                             cn : [
33103                                 {
33104                                     tag : 'i',
33105                                     cls : 'fa fa-chevron-right'
33106                                 }
33107                             ]
33108                         }
33109                     ]
33110                 }
33111             ]
33112         };
33113         
33114         return cfg;
33115     },
33116     
33117     initEvents : function()
33118     {
33119         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33120         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33121         
33122         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33123         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33124         
33125         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33126         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33127         
33128         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33129         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33130         
33131         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33132         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33133         
33134         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33135         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33136         
33137         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33138         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33139         
33140         this.thumbEl.on('click', this.onClick, this);
33141         
33142         this.prevIndicator.on('click', this.prev, this);
33143         
33144         this.nextIndicator.on('click', this.next, this);
33145         
33146     },
33147     
33148     initial : function()
33149     {
33150         if(this.files.length){
33151             this.indicator = 1;
33152             this.update()
33153         }
33154         
33155         this.fireEvent('initial', this);
33156     },
33157     
33158     update : function()
33159     {
33160         this.imageEl.attr('src', this.files[this.indicator - 1]);
33161         
33162         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33163         
33164         this.prevIndicator.show();
33165         
33166         if(this.indicator == 1){
33167             this.prevIndicator.hide();
33168         }
33169         
33170         this.nextIndicator.show();
33171         
33172         if(this.indicator == this.files.length){
33173             this.nextIndicator.hide();
33174         }
33175         
33176         this.thumbEl.scrollTo('top');
33177         
33178         this.fireEvent('update', this);
33179     },
33180     
33181     onClick : function(e)
33182     {
33183         e.preventDefault();
33184         
33185         this.fireEvent('click', this);
33186     },
33187     
33188     prev : function(e)
33189     {
33190         e.preventDefault();
33191         
33192         this.indicator = Math.max(1, this.indicator - 1);
33193         
33194         this.update();
33195     },
33196     
33197     next : function(e)
33198     {
33199         e.preventDefault();
33200         
33201         this.indicator = Math.min(this.files.length, this.indicator + 1);
33202         
33203         this.update();
33204     }
33205 });
33206 /*
33207  * - LGPL
33208  *
33209  * RadioSet
33210  *
33211  *
33212  */
33213
33214 /**
33215  * @class Roo.bootstrap.RadioSet
33216  * @extends Roo.bootstrap.Input
33217  * Bootstrap RadioSet class
33218  * @cfg {String} indicatorpos (left|right) default left
33219  * @cfg {Boolean} inline (true|false) inline the element (default true)
33220  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33221  * @constructor
33222  * Create a new RadioSet
33223  * @param {Object} config The config object
33224  */
33225
33226 Roo.bootstrap.RadioSet = function(config){
33227     
33228     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33229     
33230     this.radioes = [];
33231     
33232     Roo.bootstrap.RadioSet.register(this);
33233     
33234     this.addEvents({
33235         /**
33236         * @event check
33237         * Fires when the element is checked or unchecked.
33238         * @param {Roo.bootstrap.RadioSet} this This radio
33239         * @param {Roo.bootstrap.Radio} item The checked item
33240         */
33241        check : true
33242     });
33243     
33244 };
33245
33246 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33247
33248     radioes : false,
33249     
33250     inline : true,
33251     
33252     weight : '',
33253     
33254     indicatorpos : 'left',
33255     
33256     getAutoCreate : function()
33257     {
33258         var label = {
33259             tag : 'label',
33260             cls : 'roo-radio-set-label',
33261             cn : [
33262                 {
33263                     tag : 'span',
33264                     html : this.fieldLabel
33265                 }
33266             ]
33267         };
33268         
33269         if(this.indicatorpos == 'left'){
33270             label.cn.unshift({
33271                 tag : 'i',
33272                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33273                 tooltip : 'This field is required'
33274             });
33275         } else {
33276             label.cn.push({
33277                 tag : 'i',
33278                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33279                 tooltip : 'This field is required'
33280             });
33281         }
33282         
33283         var items = {
33284             tag : 'div',
33285             cls : 'roo-radio-set-items'
33286         };
33287         
33288         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33289         
33290         if (align === 'left' && this.fieldLabel.length) {
33291             
33292             items = {
33293                 cls : "roo-radio-set-right", 
33294                 cn: [
33295                     items
33296                 ]
33297             };
33298             
33299             if(this.labelWidth > 12){
33300                 label.style = "width: " + this.labelWidth + 'px';
33301             }
33302             
33303             if(this.labelWidth < 13 && this.labelmd == 0){
33304                 this.labelmd = this.labelWidth;
33305             }
33306             
33307             if(this.labellg > 0){
33308                 label.cls += ' col-lg-' + this.labellg;
33309                 items.cls += ' col-lg-' + (12 - this.labellg);
33310             }
33311             
33312             if(this.labelmd > 0){
33313                 label.cls += ' col-md-' + this.labelmd;
33314                 items.cls += ' col-md-' + (12 - this.labelmd);
33315             }
33316             
33317             if(this.labelsm > 0){
33318                 label.cls += ' col-sm-' + this.labelsm;
33319                 items.cls += ' col-sm-' + (12 - this.labelsm);
33320             }
33321             
33322             if(this.labelxs > 0){
33323                 label.cls += ' col-xs-' + this.labelxs;
33324                 items.cls += ' col-xs-' + (12 - this.labelxs);
33325             }
33326         }
33327         
33328         var cfg = {
33329             tag : 'div',
33330             cls : 'roo-radio-set',
33331             cn : [
33332                 {
33333                     tag : 'input',
33334                     cls : 'roo-radio-set-input',
33335                     type : 'hidden',
33336                     name : this.name,
33337                     value : this.value ? this.value :  ''
33338                 },
33339                 label,
33340                 items
33341             ]
33342         };
33343         
33344         if(this.weight.length){
33345             cfg.cls += ' roo-radio-' + this.weight;
33346         }
33347         
33348         if(this.inline) {
33349             cfg.cls += ' roo-radio-set-inline';
33350         }
33351         
33352         var settings=this;
33353         ['xs','sm','md','lg'].map(function(size){
33354             if (settings[size]) {
33355                 cfg.cls += ' col-' + size + '-' + settings[size];
33356             }
33357         });
33358         
33359         return cfg;
33360         
33361     },
33362
33363     initEvents : function()
33364     {
33365         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33366         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33367         
33368         if(!this.fieldLabel.length){
33369             this.labelEl.hide();
33370         }
33371         
33372         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33373         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33374         
33375         this.indicatorEl().addClass('invisible');
33376         
33377         this.originalValue = this.getValue();
33378         
33379     },
33380     
33381     inputEl: function ()
33382     {
33383         return this.el.select('.roo-radio-set-input', true).first();
33384     },
33385     
33386     getChildContainer : function()
33387     {
33388         return this.itemsEl;
33389     },
33390     
33391     register : function(item)
33392     {
33393         this.radioes.push(item);
33394         
33395     },
33396     
33397     validate : function()
33398     {   
33399         var valid = false;
33400         
33401         Roo.each(this.radioes, function(i){
33402             if(!i.checked){
33403                 return;
33404             }
33405             
33406             valid = true;
33407             return false;
33408         });
33409         
33410         if(this.allowBlank) {
33411             return true;
33412         }
33413         
33414         if(this.disabled || valid){
33415             this.markValid();
33416             return true;
33417         }
33418         
33419         this.markInvalid();
33420         return false;
33421         
33422     },
33423     
33424     markValid : function()
33425     {
33426         if(this.labelEl.isVisible(true)){
33427             this.indicatorEl().removeClass('visible');
33428             this.indicatorEl().addClass('invisible');
33429         }
33430         
33431         this.el.removeClass([this.invalidClass, this.validClass]);
33432         this.el.addClass(this.validClass);
33433         
33434         this.fireEvent('valid', this);
33435     },
33436     
33437     markInvalid : function(msg)
33438     {
33439         if(this.allowBlank || this.disabled){
33440             return;
33441         }
33442         
33443         if(this.labelEl.isVisible(true)){
33444             this.indicatorEl().removeClass('invisible');
33445             this.indicatorEl().addClass('visible');
33446         }
33447         
33448         this.el.removeClass([this.invalidClass, this.validClass]);
33449         this.el.addClass(this.invalidClass);
33450         
33451         this.fireEvent('invalid', this, msg);
33452         
33453     },
33454     
33455     setValue : function(v, suppressEvent)
33456     {   
33457         this.value = v;
33458         if(this.rendered){
33459             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33460         }
33461         
33462         Roo.each(this.radioes, function(i){
33463             
33464             i.checked = false;
33465             i.el.removeClass('checked');
33466             
33467             if(i.value === v || i.value.toString() === v.toString()){
33468                 i.checked = true;
33469                 i.el.addClass('checked');
33470                 
33471                 if(suppressEvent !== true){
33472                     this.fireEvent('check', this, i);
33473                 }
33474             }
33475             
33476         }, this);
33477         
33478         this.validate();
33479     },
33480     
33481     clearInvalid : function(){
33482         
33483         if(!this.el || this.preventMark){
33484             return;
33485         }
33486         
33487         this.el.removeClass([this.invalidClass]);
33488         
33489         this.fireEvent('valid', this);
33490     }
33491     
33492 });
33493
33494 Roo.apply(Roo.bootstrap.RadioSet, {
33495     
33496     groups: {},
33497     
33498     register : function(set)
33499     {
33500         this.groups[set.name] = set;
33501     },
33502     
33503     get: function(name) 
33504     {
33505         if (typeof(this.groups[name]) == 'undefined') {
33506             return false;
33507         }
33508         
33509         return this.groups[name] ;
33510     }
33511     
33512 });
33513 /*
33514  * Based on:
33515  * Ext JS Library 1.1.1
33516  * Copyright(c) 2006-2007, Ext JS, LLC.
33517  *
33518  * Originally Released Under LGPL - original licence link has changed is not relivant.
33519  *
33520  * Fork - LGPL
33521  * <script type="text/javascript">
33522  */
33523
33524
33525 /**
33526  * @class Roo.bootstrap.SplitBar
33527  * @extends Roo.util.Observable
33528  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33529  * <br><br>
33530  * Usage:
33531  * <pre><code>
33532 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33533                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33534 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33535 split.minSize = 100;
33536 split.maxSize = 600;
33537 split.animate = true;
33538 split.on('moved', splitterMoved);
33539 </code></pre>
33540  * @constructor
33541  * Create a new SplitBar
33542  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33543  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33544  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33545  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33546                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33547                         position of the SplitBar).
33548  */
33549 Roo.bootstrap.SplitBar = function(cfg){
33550     
33551     /** @private */
33552     
33553     //{
33554     //  dragElement : elm
33555     //  resizingElement: el,
33556         // optional..
33557     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33558     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33559         // existingProxy ???
33560     //}
33561     
33562     this.el = Roo.get(cfg.dragElement, true);
33563     this.el.dom.unselectable = "on";
33564     /** @private */
33565     this.resizingEl = Roo.get(cfg.resizingElement, true);
33566
33567     /**
33568      * @private
33569      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33570      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33571      * @type Number
33572      */
33573     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33574     
33575     /**
33576      * The minimum size of the resizing element. (Defaults to 0)
33577      * @type Number
33578      */
33579     this.minSize = 0;
33580     
33581     /**
33582      * The maximum size of the resizing element. (Defaults to 2000)
33583      * @type Number
33584      */
33585     this.maxSize = 2000;
33586     
33587     /**
33588      * Whether to animate the transition to the new size
33589      * @type Boolean
33590      */
33591     this.animate = false;
33592     
33593     /**
33594      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33595      * @type Boolean
33596      */
33597     this.useShim = false;
33598     
33599     /** @private */
33600     this.shim = null;
33601     
33602     if(!cfg.existingProxy){
33603         /** @private */
33604         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33605     }else{
33606         this.proxy = Roo.get(cfg.existingProxy).dom;
33607     }
33608     /** @private */
33609     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33610     
33611     /** @private */
33612     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33613     
33614     /** @private */
33615     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33616     
33617     /** @private */
33618     this.dragSpecs = {};
33619     
33620     /**
33621      * @private The adapter to use to positon and resize elements
33622      */
33623     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33624     this.adapter.init(this);
33625     
33626     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33627         /** @private */
33628         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33629         this.el.addClass("roo-splitbar-h");
33630     }else{
33631         /** @private */
33632         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33633         this.el.addClass("roo-splitbar-v");
33634     }
33635     
33636     this.addEvents({
33637         /**
33638          * @event resize
33639          * Fires when the splitter is moved (alias for {@link #event-moved})
33640          * @param {Roo.bootstrap.SplitBar} this
33641          * @param {Number} newSize the new width or height
33642          */
33643         "resize" : true,
33644         /**
33645          * @event moved
33646          * Fires when the splitter is moved
33647          * @param {Roo.bootstrap.SplitBar} this
33648          * @param {Number} newSize the new width or height
33649          */
33650         "moved" : true,
33651         /**
33652          * @event beforeresize
33653          * Fires before the splitter is dragged
33654          * @param {Roo.bootstrap.SplitBar} this
33655          */
33656         "beforeresize" : true,
33657
33658         "beforeapply" : true
33659     });
33660
33661     Roo.util.Observable.call(this);
33662 };
33663
33664 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33665     onStartProxyDrag : function(x, y){
33666         this.fireEvent("beforeresize", this);
33667         if(!this.overlay){
33668             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33669             o.unselectable();
33670             o.enableDisplayMode("block");
33671             // all splitbars share the same overlay
33672             Roo.bootstrap.SplitBar.prototype.overlay = o;
33673         }
33674         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33675         this.overlay.show();
33676         Roo.get(this.proxy).setDisplayed("block");
33677         var size = this.adapter.getElementSize(this);
33678         this.activeMinSize = this.getMinimumSize();;
33679         this.activeMaxSize = this.getMaximumSize();;
33680         var c1 = size - this.activeMinSize;
33681         var c2 = Math.max(this.activeMaxSize - size, 0);
33682         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33683             this.dd.resetConstraints();
33684             this.dd.setXConstraint(
33685                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33686                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33687             );
33688             this.dd.setYConstraint(0, 0);
33689         }else{
33690             this.dd.resetConstraints();
33691             this.dd.setXConstraint(0, 0);
33692             this.dd.setYConstraint(
33693                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33694                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33695             );
33696          }
33697         this.dragSpecs.startSize = size;
33698         this.dragSpecs.startPoint = [x, y];
33699         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33700     },
33701     
33702     /** 
33703      * @private Called after the drag operation by the DDProxy
33704      */
33705     onEndProxyDrag : function(e){
33706         Roo.get(this.proxy).setDisplayed(false);
33707         var endPoint = Roo.lib.Event.getXY(e);
33708         if(this.overlay){
33709             this.overlay.hide();
33710         }
33711         var newSize;
33712         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33713             newSize = this.dragSpecs.startSize + 
33714                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33715                     endPoint[0] - this.dragSpecs.startPoint[0] :
33716                     this.dragSpecs.startPoint[0] - endPoint[0]
33717                 );
33718         }else{
33719             newSize = this.dragSpecs.startSize + 
33720                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33721                     endPoint[1] - this.dragSpecs.startPoint[1] :
33722                     this.dragSpecs.startPoint[1] - endPoint[1]
33723                 );
33724         }
33725         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33726         if(newSize != this.dragSpecs.startSize){
33727             if(this.fireEvent('beforeapply', this, newSize) !== false){
33728                 this.adapter.setElementSize(this, newSize);
33729                 this.fireEvent("moved", this, newSize);
33730                 this.fireEvent("resize", this, newSize);
33731             }
33732         }
33733     },
33734     
33735     /**
33736      * Get the adapter this SplitBar uses
33737      * @return The adapter object
33738      */
33739     getAdapter : function(){
33740         return this.adapter;
33741     },
33742     
33743     /**
33744      * Set the adapter this SplitBar uses
33745      * @param {Object} adapter A SplitBar adapter object
33746      */
33747     setAdapter : function(adapter){
33748         this.adapter = adapter;
33749         this.adapter.init(this);
33750     },
33751     
33752     /**
33753      * Gets the minimum size for the resizing element
33754      * @return {Number} The minimum size
33755      */
33756     getMinimumSize : function(){
33757         return this.minSize;
33758     },
33759     
33760     /**
33761      * Sets the minimum size for the resizing element
33762      * @param {Number} minSize The minimum size
33763      */
33764     setMinimumSize : function(minSize){
33765         this.minSize = minSize;
33766     },
33767     
33768     /**
33769      * Gets the maximum size for the resizing element
33770      * @return {Number} The maximum size
33771      */
33772     getMaximumSize : function(){
33773         return this.maxSize;
33774     },
33775     
33776     /**
33777      * Sets the maximum size for the resizing element
33778      * @param {Number} maxSize The maximum size
33779      */
33780     setMaximumSize : function(maxSize){
33781         this.maxSize = maxSize;
33782     },
33783     
33784     /**
33785      * Sets the initialize size for the resizing element
33786      * @param {Number} size The initial size
33787      */
33788     setCurrentSize : function(size){
33789         var oldAnimate = this.animate;
33790         this.animate = false;
33791         this.adapter.setElementSize(this, size);
33792         this.animate = oldAnimate;
33793     },
33794     
33795     /**
33796      * Destroy this splitbar. 
33797      * @param {Boolean} removeEl True to remove the element
33798      */
33799     destroy : function(removeEl){
33800         if(this.shim){
33801             this.shim.remove();
33802         }
33803         this.dd.unreg();
33804         this.proxy.parentNode.removeChild(this.proxy);
33805         if(removeEl){
33806             this.el.remove();
33807         }
33808     }
33809 });
33810
33811 /**
33812  * @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.
33813  */
33814 Roo.bootstrap.SplitBar.createProxy = function(dir){
33815     var proxy = new Roo.Element(document.createElement("div"));
33816     proxy.unselectable();
33817     var cls = 'roo-splitbar-proxy';
33818     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33819     document.body.appendChild(proxy.dom);
33820     return proxy.dom;
33821 };
33822
33823 /** 
33824  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33825  * Default Adapter. It assumes the splitter and resizing element are not positioned
33826  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33827  */
33828 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33829 };
33830
33831 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33832     // do nothing for now
33833     init : function(s){
33834     
33835     },
33836     /**
33837      * Called before drag operations to get the current size of the resizing element. 
33838      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33839      */
33840      getElementSize : function(s){
33841         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33842             return s.resizingEl.getWidth();
33843         }else{
33844             return s.resizingEl.getHeight();
33845         }
33846     },
33847     
33848     /**
33849      * Called after drag operations to set the size of the resizing element.
33850      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33851      * @param {Number} newSize The new size to set
33852      * @param {Function} onComplete A function to be invoked when resizing is complete
33853      */
33854     setElementSize : function(s, newSize, onComplete){
33855         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33856             if(!s.animate){
33857                 s.resizingEl.setWidth(newSize);
33858                 if(onComplete){
33859                     onComplete(s, newSize);
33860                 }
33861             }else{
33862                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33863             }
33864         }else{
33865             
33866             if(!s.animate){
33867                 s.resizingEl.setHeight(newSize);
33868                 if(onComplete){
33869                     onComplete(s, newSize);
33870                 }
33871             }else{
33872                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33873             }
33874         }
33875     }
33876 };
33877
33878 /** 
33879  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33880  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33881  * Adapter that  moves the splitter element to align with the resized sizing element. 
33882  * Used with an absolute positioned SplitBar.
33883  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33884  * document.body, make sure you assign an id to the body element.
33885  */
33886 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33887     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33888     this.container = Roo.get(container);
33889 };
33890
33891 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33892     init : function(s){
33893         this.basic.init(s);
33894     },
33895     
33896     getElementSize : function(s){
33897         return this.basic.getElementSize(s);
33898     },
33899     
33900     setElementSize : function(s, newSize, onComplete){
33901         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33902     },
33903     
33904     moveSplitter : function(s){
33905         var yes = Roo.bootstrap.SplitBar;
33906         switch(s.placement){
33907             case yes.LEFT:
33908                 s.el.setX(s.resizingEl.getRight());
33909                 break;
33910             case yes.RIGHT:
33911                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33912                 break;
33913             case yes.TOP:
33914                 s.el.setY(s.resizingEl.getBottom());
33915                 break;
33916             case yes.BOTTOM:
33917                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33918                 break;
33919         }
33920     }
33921 };
33922
33923 /**
33924  * Orientation constant - Create a vertical SplitBar
33925  * @static
33926  * @type Number
33927  */
33928 Roo.bootstrap.SplitBar.VERTICAL = 1;
33929
33930 /**
33931  * Orientation constant - Create a horizontal SplitBar
33932  * @static
33933  * @type Number
33934  */
33935 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33936
33937 /**
33938  * Placement constant - The resizing element is to the left of the splitter element
33939  * @static
33940  * @type Number
33941  */
33942 Roo.bootstrap.SplitBar.LEFT = 1;
33943
33944 /**
33945  * Placement constant - The resizing element is to the right of the splitter element
33946  * @static
33947  * @type Number
33948  */
33949 Roo.bootstrap.SplitBar.RIGHT = 2;
33950
33951 /**
33952  * Placement constant - The resizing element is positioned above the splitter element
33953  * @static
33954  * @type Number
33955  */
33956 Roo.bootstrap.SplitBar.TOP = 3;
33957
33958 /**
33959  * Placement constant - The resizing element is positioned under splitter element
33960  * @static
33961  * @type Number
33962  */
33963 Roo.bootstrap.SplitBar.BOTTOM = 4;
33964 Roo.namespace("Roo.bootstrap.layout");/*
33965  * Based on:
33966  * Ext JS Library 1.1.1
33967  * Copyright(c) 2006-2007, Ext JS, LLC.
33968  *
33969  * Originally Released Under LGPL - original licence link has changed is not relivant.
33970  *
33971  * Fork - LGPL
33972  * <script type="text/javascript">
33973  */
33974
33975 /**
33976  * @class Roo.bootstrap.layout.Manager
33977  * @extends Roo.bootstrap.Component
33978  * Base class for layout managers.
33979  */
33980 Roo.bootstrap.layout.Manager = function(config)
33981 {
33982     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33983
33984
33985
33986
33987
33988     /** false to disable window resize monitoring @type Boolean */
33989     this.monitorWindowResize = true;
33990     this.regions = {};
33991     this.addEvents({
33992         /**
33993          * @event layout
33994          * Fires when a layout is performed.
33995          * @param {Roo.LayoutManager} this
33996          */
33997         "layout" : true,
33998         /**
33999          * @event regionresized
34000          * Fires when the user resizes a region.
34001          * @param {Roo.LayoutRegion} region The resized region
34002          * @param {Number} newSize The new size (width for east/west, height for north/south)
34003          */
34004         "regionresized" : true,
34005         /**
34006          * @event regioncollapsed
34007          * Fires when a region is collapsed.
34008          * @param {Roo.LayoutRegion} region The collapsed region
34009          */
34010         "regioncollapsed" : true,
34011         /**
34012          * @event regionexpanded
34013          * Fires when a region is expanded.
34014          * @param {Roo.LayoutRegion} region The expanded region
34015          */
34016         "regionexpanded" : true
34017     });
34018     this.updating = false;
34019
34020     if (config.el) {
34021         this.el = Roo.get(config.el);
34022         this.initEvents();
34023     }
34024
34025 };
34026
34027 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34028
34029
34030     regions : null,
34031
34032     monitorWindowResize : true,
34033
34034
34035     updating : false,
34036
34037
34038     onRender : function(ct, position)
34039     {
34040         if(!this.el){
34041             this.el = Roo.get(ct);
34042             this.initEvents();
34043         }
34044         //this.fireEvent('render',this);
34045     },
34046
34047
34048     initEvents: function()
34049     {
34050
34051
34052         // ie scrollbar fix
34053         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34054             document.body.scroll = "no";
34055         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34056             this.el.position('relative');
34057         }
34058         this.id = this.el.id;
34059         this.el.addClass("roo-layout-container");
34060         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34061         if(this.el.dom != document.body ) {
34062             this.el.on('resize', this.layout,this);
34063             this.el.on('show', this.layout,this);
34064         }
34065
34066     },
34067
34068     /**
34069      * Returns true if this layout is currently being updated
34070      * @return {Boolean}
34071      */
34072     isUpdating : function(){
34073         return this.updating;
34074     },
34075
34076     /**
34077      * Suspend the LayoutManager from doing auto-layouts while
34078      * making multiple add or remove calls
34079      */
34080     beginUpdate : function(){
34081         this.updating = true;
34082     },
34083
34084     /**
34085      * Restore auto-layouts and optionally disable the manager from performing a layout
34086      * @param {Boolean} noLayout true to disable a layout update
34087      */
34088     endUpdate : function(noLayout){
34089         this.updating = false;
34090         if(!noLayout){
34091             this.layout();
34092         }
34093     },
34094
34095     layout: function(){
34096         // abstract...
34097     },
34098
34099     onRegionResized : function(region, newSize){
34100         this.fireEvent("regionresized", region, newSize);
34101         this.layout();
34102     },
34103
34104     onRegionCollapsed : function(region){
34105         this.fireEvent("regioncollapsed", region);
34106     },
34107
34108     onRegionExpanded : function(region){
34109         this.fireEvent("regionexpanded", region);
34110     },
34111
34112     /**
34113      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34114      * performs box-model adjustments.
34115      * @return {Object} The size as an object {width: (the width), height: (the height)}
34116      */
34117     getViewSize : function()
34118     {
34119         var size;
34120         if(this.el.dom != document.body){
34121             size = this.el.getSize();
34122         }else{
34123             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34124         }
34125         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34126         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34127         return size;
34128     },
34129
34130     /**
34131      * Returns the Element this layout is bound to.
34132      * @return {Roo.Element}
34133      */
34134     getEl : function(){
34135         return this.el;
34136     },
34137
34138     /**
34139      * Returns the specified region.
34140      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34141      * @return {Roo.LayoutRegion}
34142      */
34143     getRegion : function(target){
34144         return this.regions[target.toLowerCase()];
34145     },
34146
34147     onWindowResize : function(){
34148         if(this.monitorWindowResize){
34149             this.layout();
34150         }
34151     }
34152 });
34153 /*
34154  * Based on:
34155  * Ext JS Library 1.1.1
34156  * Copyright(c) 2006-2007, Ext JS, LLC.
34157  *
34158  * Originally Released Under LGPL - original licence link has changed is not relivant.
34159  *
34160  * Fork - LGPL
34161  * <script type="text/javascript">
34162  */
34163 /**
34164  * @class Roo.bootstrap.layout.Border
34165  * @extends Roo.bootstrap.layout.Manager
34166  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34167  * please see: examples/bootstrap/nested.html<br><br>
34168  
34169 <b>The container the layout is rendered into can be either the body element or any other element.
34170 If it is not the body element, the container needs to either be an absolute positioned element,
34171 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34172 the container size if it is not the body element.</b>
34173
34174 * @constructor
34175 * Create a new Border
34176 * @param {Object} config Configuration options
34177  */
34178 Roo.bootstrap.layout.Border = function(config){
34179     config = config || {};
34180     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34181     
34182     
34183     
34184     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34185         if(config[region]){
34186             config[region].region = region;
34187             this.addRegion(config[region]);
34188         }
34189     },this);
34190     
34191 };
34192
34193 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34194
34195 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34196     /**
34197      * Creates and adds a new region if it doesn't already exist.
34198      * @param {String} target The target region key (north, south, east, west or center).
34199      * @param {Object} config The regions config object
34200      * @return {BorderLayoutRegion} The new region
34201      */
34202     addRegion : function(config)
34203     {
34204         if(!this.regions[config.region]){
34205             var r = this.factory(config);
34206             this.bindRegion(r);
34207         }
34208         return this.regions[config.region];
34209     },
34210
34211     // private (kinda)
34212     bindRegion : function(r){
34213         this.regions[r.config.region] = r;
34214         
34215         r.on("visibilitychange",    this.layout, this);
34216         r.on("paneladded",          this.layout, this);
34217         r.on("panelremoved",        this.layout, this);
34218         r.on("invalidated",         this.layout, this);
34219         r.on("resized",             this.onRegionResized, this);
34220         r.on("collapsed",           this.onRegionCollapsed, this);
34221         r.on("expanded",            this.onRegionExpanded, this);
34222     },
34223
34224     /**
34225      * Performs a layout update.
34226      */
34227     layout : function()
34228     {
34229         if(this.updating) {
34230             return;
34231         }
34232         
34233         // render all the rebions if they have not been done alreayd?
34234         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34235             if(this.regions[region] && !this.regions[region].bodyEl){
34236                 this.regions[region].onRender(this.el)
34237             }
34238         },this);
34239         
34240         var size = this.getViewSize();
34241         var w = size.width;
34242         var h = size.height;
34243         var centerW = w;
34244         var centerH = h;
34245         var centerY = 0;
34246         var centerX = 0;
34247         //var x = 0, y = 0;
34248
34249         var rs = this.regions;
34250         var north = rs["north"];
34251         var south = rs["south"]; 
34252         var west = rs["west"];
34253         var east = rs["east"];
34254         var center = rs["center"];
34255         //if(this.hideOnLayout){ // not supported anymore
34256             //c.el.setStyle("display", "none");
34257         //}
34258         if(north && north.isVisible()){
34259             var b = north.getBox();
34260             var m = north.getMargins();
34261             b.width = w - (m.left+m.right);
34262             b.x = m.left;
34263             b.y = m.top;
34264             centerY = b.height + b.y + m.bottom;
34265             centerH -= centerY;
34266             north.updateBox(this.safeBox(b));
34267         }
34268         if(south && south.isVisible()){
34269             var b = south.getBox();
34270             var m = south.getMargins();
34271             b.width = w - (m.left+m.right);
34272             b.x = m.left;
34273             var totalHeight = (b.height + m.top + m.bottom);
34274             b.y = h - totalHeight + m.top;
34275             centerH -= totalHeight;
34276             south.updateBox(this.safeBox(b));
34277         }
34278         if(west && west.isVisible()){
34279             var b = west.getBox();
34280             var m = west.getMargins();
34281             b.height = centerH - (m.top+m.bottom);
34282             b.x = m.left;
34283             b.y = centerY + m.top;
34284             var totalWidth = (b.width + m.left + m.right);
34285             centerX += totalWidth;
34286             centerW -= totalWidth;
34287             west.updateBox(this.safeBox(b));
34288         }
34289         if(east && east.isVisible()){
34290             var b = east.getBox();
34291             var m = east.getMargins();
34292             b.height = centerH - (m.top+m.bottom);
34293             var totalWidth = (b.width + m.left + m.right);
34294             b.x = w - totalWidth + m.left;
34295             b.y = centerY + m.top;
34296             centerW -= totalWidth;
34297             east.updateBox(this.safeBox(b));
34298         }
34299         if(center){
34300             var m = center.getMargins();
34301             var centerBox = {
34302                 x: centerX + m.left,
34303                 y: centerY + m.top,
34304                 width: centerW - (m.left+m.right),
34305                 height: centerH - (m.top+m.bottom)
34306             };
34307             //if(this.hideOnLayout){
34308                 //center.el.setStyle("display", "block");
34309             //}
34310             center.updateBox(this.safeBox(centerBox));
34311         }
34312         this.el.repaint();
34313         this.fireEvent("layout", this);
34314     },
34315
34316     // private
34317     safeBox : function(box){
34318         box.width = Math.max(0, box.width);
34319         box.height = Math.max(0, box.height);
34320         return box;
34321     },
34322
34323     /**
34324      * Adds a ContentPanel (or subclass) to this layout.
34325      * @param {String} target The target region key (north, south, east, west or center).
34326      * @param {Roo.ContentPanel} panel The panel to add
34327      * @return {Roo.ContentPanel} The added panel
34328      */
34329     add : function(target, panel){
34330          
34331         target = target.toLowerCase();
34332         return this.regions[target].add(panel);
34333     },
34334
34335     /**
34336      * Remove a ContentPanel (or subclass) to this layout.
34337      * @param {String} target The target region key (north, south, east, west or center).
34338      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34339      * @return {Roo.ContentPanel} The removed panel
34340      */
34341     remove : function(target, panel){
34342         target = target.toLowerCase();
34343         return this.regions[target].remove(panel);
34344     },
34345
34346     /**
34347      * Searches all regions for a panel with the specified id
34348      * @param {String} panelId
34349      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34350      */
34351     findPanel : function(panelId){
34352         var rs = this.regions;
34353         for(var target in rs){
34354             if(typeof rs[target] != "function"){
34355                 var p = rs[target].getPanel(panelId);
34356                 if(p){
34357                     return p;
34358                 }
34359             }
34360         }
34361         return null;
34362     },
34363
34364     /**
34365      * Searches all regions for a panel with the specified id and activates (shows) it.
34366      * @param {String/ContentPanel} panelId The panels id or the panel itself
34367      * @return {Roo.ContentPanel} The shown panel or null
34368      */
34369     showPanel : function(panelId) {
34370       var rs = this.regions;
34371       for(var target in rs){
34372          var r = rs[target];
34373          if(typeof r != "function"){
34374             if(r.hasPanel(panelId)){
34375                return r.showPanel(panelId);
34376             }
34377          }
34378       }
34379       return null;
34380    },
34381
34382    /**
34383      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34384      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34385      */
34386    /*
34387     restoreState : function(provider){
34388         if(!provider){
34389             provider = Roo.state.Manager;
34390         }
34391         var sm = new Roo.LayoutStateManager();
34392         sm.init(this, provider);
34393     },
34394 */
34395  
34396  
34397     /**
34398      * Adds a xtype elements to the layout.
34399      * <pre><code>
34400
34401 layout.addxtype({
34402        xtype : 'ContentPanel',
34403        region: 'west',
34404        items: [ .... ]
34405    }
34406 );
34407
34408 layout.addxtype({
34409         xtype : 'NestedLayoutPanel',
34410         region: 'west',
34411         layout: {
34412            center: { },
34413            west: { }   
34414         },
34415         items : [ ... list of content panels or nested layout panels.. ]
34416    }
34417 );
34418 </code></pre>
34419      * @param {Object} cfg Xtype definition of item to add.
34420      */
34421     addxtype : function(cfg)
34422     {
34423         // basically accepts a pannel...
34424         // can accept a layout region..!?!?
34425         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34426         
34427         
34428         // theory?  children can only be panels??
34429         
34430         //if (!cfg.xtype.match(/Panel$/)) {
34431         //    return false;
34432         //}
34433         var ret = false;
34434         
34435         if (typeof(cfg.region) == 'undefined') {
34436             Roo.log("Failed to add Panel, region was not set");
34437             Roo.log(cfg);
34438             return false;
34439         }
34440         var region = cfg.region;
34441         delete cfg.region;
34442         
34443           
34444         var xitems = [];
34445         if (cfg.items) {
34446             xitems = cfg.items;
34447             delete cfg.items;
34448         }
34449         var nb = false;
34450         
34451         switch(cfg.xtype) 
34452         {
34453             case 'Content':  // ContentPanel (el, cfg)
34454             case 'Scroll':  // ContentPanel (el, cfg)
34455             case 'View': 
34456                 cfg.autoCreate = true;
34457                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34458                 //} else {
34459                 //    var el = this.el.createChild();
34460                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34461                 //}
34462                 
34463                 this.add(region, ret);
34464                 break;
34465             
34466             /*
34467             case 'TreePanel': // our new panel!
34468                 cfg.el = this.el.createChild();
34469                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34470                 this.add(region, ret);
34471                 break;
34472             */
34473             
34474             case 'Nest': 
34475                 // create a new Layout (which is  a Border Layout...
34476                 
34477                 var clayout = cfg.layout;
34478                 clayout.el  = this.el.createChild();
34479                 clayout.items   = clayout.items  || [];
34480                 
34481                 delete cfg.layout;
34482                 
34483                 // replace this exitems with the clayout ones..
34484                 xitems = clayout.items;
34485                  
34486                 // force background off if it's in center...
34487                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34488                     cfg.background = false;
34489                 }
34490                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34491                 
34492                 
34493                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34494                 //console.log('adding nested layout panel '  + cfg.toSource());
34495                 this.add(region, ret);
34496                 nb = {}; /// find first...
34497                 break;
34498             
34499             case 'Grid':
34500                 
34501                 // needs grid and region
34502                 
34503                 //var el = this.getRegion(region).el.createChild();
34504                 /*
34505                  *var el = this.el.createChild();
34506                 // create the grid first...
34507                 cfg.grid.container = el;
34508                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34509                 */
34510                 
34511                 if (region == 'center' && this.active ) {
34512                     cfg.background = false;
34513                 }
34514                 
34515                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34516                 
34517                 this.add(region, ret);
34518                 /*
34519                 if (cfg.background) {
34520                     // render grid on panel activation (if panel background)
34521                     ret.on('activate', function(gp) {
34522                         if (!gp.grid.rendered) {
34523                     //        gp.grid.render(el);
34524                         }
34525                     });
34526                 } else {
34527                   //  cfg.grid.render(el);
34528                 }
34529                 */
34530                 break;
34531            
34532            
34533             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34534                 // it was the old xcomponent building that caused this before.
34535                 // espeically if border is the top element in the tree.
34536                 ret = this;
34537                 break; 
34538                 
34539                     
34540                 
34541                 
34542                 
34543             default:
34544                 /*
34545                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34546                     
34547                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34548                     this.add(region, ret);
34549                 } else {
34550                 */
34551                     Roo.log(cfg);
34552                     throw "Can not add '" + cfg.xtype + "' to Border";
34553                     return null;
34554              
34555                                 
34556              
34557         }
34558         this.beginUpdate();
34559         // add children..
34560         var region = '';
34561         var abn = {};
34562         Roo.each(xitems, function(i)  {
34563             region = nb && i.region ? i.region : false;
34564             
34565             var add = ret.addxtype(i);
34566            
34567             if (region) {
34568                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34569                 if (!i.background) {
34570                     abn[region] = nb[region] ;
34571                 }
34572             }
34573             
34574         });
34575         this.endUpdate();
34576
34577         // make the last non-background panel active..
34578         //if (nb) { Roo.log(abn); }
34579         if (nb) {
34580             
34581             for(var r in abn) {
34582                 region = this.getRegion(r);
34583                 if (region) {
34584                     // tried using nb[r], but it does not work..
34585                      
34586                     region.showPanel(abn[r]);
34587                    
34588                 }
34589             }
34590         }
34591         return ret;
34592         
34593     },
34594     
34595     
34596 // private
34597     factory : function(cfg)
34598     {
34599         
34600         var validRegions = Roo.bootstrap.layout.Border.regions;
34601
34602         var target = cfg.region;
34603         cfg.mgr = this;
34604         
34605         var r = Roo.bootstrap.layout;
34606         Roo.log(target);
34607         switch(target){
34608             case "north":
34609                 return new r.North(cfg);
34610             case "south":
34611                 return new r.South(cfg);
34612             case "east":
34613                 return new r.East(cfg);
34614             case "west":
34615                 return new r.West(cfg);
34616             case "center":
34617                 return new r.Center(cfg);
34618         }
34619         throw 'Layout region "'+target+'" not supported.';
34620     }
34621     
34622     
34623 });
34624  /*
34625  * Based on:
34626  * Ext JS Library 1.1.1
34627  * Copyright(c) 2006-2007, Ext JS, LLC.
34628  *
34629  * Originally Released Under LGPL - original licence link has changed is not relivant.
34630  *
34631  * Fork - LGPL
34632  * <script type="text/javascript">
34633  */
34634  
34635 /**
34636  * @class Roo.bootstrap.layout.Basic
34637  * @extends Roo.util.Observable
34638  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34639  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34640  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34641  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34642  * @cfg {string}   region  the region that it inhabits..
34643  * @cfg {bool}   skipConfig skip config?
34644  * 
34645
34646  */
34647 Roo.bootstrap.layout.Basic = function(config){
34648     
34649     this.mgr = config.mgr;
34650     
34651     this.position = config.region;
34652     
34653     var skipConfig = config.skipConfig;
34654     
34655     this.events = {
34656         /**
34657          * @scope Roo.BasicLayoutRegion
34658          */
34659         
34660         /**
34661          * @event beforeremove
34662          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34663          * @param {Roo.LayoutRegion} this
34664          * @param {Roo.ContentPanel} panel The panel
34665          * @param {Object} e The cancel event object
34666          */
34667         "beforeremove" : true,
34668         /**
34669          * @event invalidated
34670          * Fires when the layout for this region is changed.
34671          * @param {Roo.LayoutRegion} this
34672          */
34673         "invalidated" : true,
34674         /**
34675          * @event visibilitychange
34676          * Fires when this region is shown or hidden 
34677          * @param {Roo.LayoutRegion} this
34678          * @param {Boolean} visibility true or false
34679          */
34680         "visibilitychange" : true,
34681         /**
34682          * @event paneladded
34683          * Fires when a panel is added. 
34684          * @param {Roo.LayoutRegion} this
34685          * @param {Roo.ContentPanel} panel The panel
34686          */
34687         "paneladded" : true,
34688         /**
34689          * @event panelremoved
34690          * Fires when a panel is removed. 
34691          * @param {Roo.LayoutRegion} this
34692          * @param {Roo.ContentPanel} panel The panel
34693          */
34694         "panelremoved" : true,
34695         /**
34696          * @event beforecollapse
34697          * Fires when this region before collapse.
34698          * @param {Roo.LayoutRegion} this
34699          */
34700         "beforecollapse" : true,
34701         /**
34702          * @event collapsed
34703          * Fires when this region is collapsed.
34704          * @param {Roo.LayoutRegion} this
34705          */
34706         "collapsed" : true,
34707         /**
34708          * @event expanded
34709          * Fires when this region is expanded.
34710          * @param {Roo.LayoutRegion} this
34711          */
34712         "expanded" : true,
34713         /**
34714          * @event slideshow
34715          * Fires when this region is slid into view.
34716          * @param {Roo.LayoutRegion} this
34717          */
34718         "slideshow" : true,
34719         /**
34720          * @event slidehide
34721          * Fires when this region slides out of view. 
34722          * @param {Roo.LayoutRegion} this
34723          */
34724         "slidehide" : true,
34725         /**
34726          * @event panelactivated
34727          * Fires when a panel is activated. 
34728          * @param {Roo.LayoutRegion} this
34729          * @param {Roo.ContentPanel} panel The activated panel
34730          */
34731         "panelactivated" : true,
34732         /**
34733          * @event resized
34734          * Fires when the user resizes this region. 
34735          * @param {Roo.LayoutRegion} this
34736          * @param {Number} newSize The new size (width for east/west, height for north/south)
34737          */
34738         "resized" : true
34739     };
34740     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34741     this.panels = new Roo.util.MixedCollection();
34742     this.panels.getKey = this.getPanelId.createDelegate(this);
34743     this.box = null;
34744     this.activePanel = null;
34745     // ensure listeners are added...
34746     
34747     if (config.listeners || config.events) {
34748         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34749             listeners : config.listeners || {},
34750             events : config.events || {}
34751         });
34752     }
34753     
34754     if(skipConfig !== true){
34755         this.applyConfig(config);
34756     }
34757 };
34758
34759 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34760 {
34761     getPanelId : function(p){
34762         return p.getId();
34763     },
34764     
34765     applyConfig : function(config){
34766         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34767         this.config = config;
34768         
34769     },
34770     
34771     /**
34772      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34773      * the width, for horizontal (north, south) the height.
34774      * @param {Number} newSize The new width or height
34775      */
34776     resizeTo : function(newSize){
34777         var el = this.el ? this.el :
34778                  (this.activePanel ? this.activePanel.getEl() : null);
34779         if(el){
34780             switch(this.position){
34781                 case "east":
34782                 case "west":
34783                     el.setWidth(newSize);
34784                     this.fireEvent("resized", this, newSize);
34785                 break;
34786                 case "north":
34787                 case "south":
34788                     el.setHeight(newSize);
34789                     this.fireEvent("resized", this, newSize);
34790                 break;                
34791             }
34792         }
34793     },
34794     
34795     getBox : function(){
34796         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34797     },
34798     
34799     getMargins : function(){
34800         return this.margins;
34801     },
34802     
34803     updateBox : function(box){
34804         this.box = box;
34805         var el = this.activePanel.getEl();
34806         el.dom.style.left = box.x + "px";
34807         el.dom.style.top = box.y + "px";
34808         this.activePanel.setSize(box.width, box.height);
34809     },
34810     
34811     /**
34812      * Returns the container element for this region.
34813      * @return {Roo.Element}
34814      */
34815     getEl : function(){
34816         return this.activePanel;
34817     },
34818     
34819     /**
34820      * Returns true if this region is currently visible.
34821      * @return {Boolean}
34822      */
34823     isVisible : function(){
34824         return this.activePanel ? true : false;
34825     },
34826     
34827     setActivePanel : function(panel){
34828         panel = this.getPanel(panel);
34829         if(this.activePanel && this.activePanel != panel){
34830             this.activePanel.setActiveState(false);
34831             this.activePanel.getEl().setLeftTop(-10000,-10000);
34832         }
34833         this.activePanel = panel;
34834         panel.setActiveState(true);
34835         if(this.box){
34836             panel.setSize(this.box.width, this.box.height);
34837         }
34838         this.fireEvent("panelactivated", this, panel);
34839         this.fireEvent("invalidated");
34840     },
34841     
34842     /**
34843      * Show the specified panel.
34844      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34845      * @return {Roo.ContentPanel} The shown panel or null
34846      */
34847     showPanel : function(panel){
34848         panel = this.getPanel(panel);
34849         if(panel){
34850             this.setActivePanel(panel);
34851         }
34852         return panel;
34853     },
34854     
34855     /**
34856      * Get the active panel for this region.
34857      * @return {Roo.ContentPanel} The active panel or null
34858      */
34859     getActivePanel : function(){
34860         return this.activePanel;
34861     },
34862     
34863     /**
34864      * Add the passed ContentPanel(s)
34865      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34866      * @return {Roo.ContentPanel} The panel added (if only one was added)
34867      */
34868     add : function(panel){
34869         if(arguments.length > 1){
34870             for(var i = 0, len = arguments.length; i < len; i++) {
34871                 this.add(arguments[i]);
34872             }
34873             return null;
34874         }
34875         if(this.hasPanel(panel)){
34876             this.showPanel(panel);
34877             return panel;
34878         }
34879         var el = panel.getEl();
34880         if(el.dom.parentNode != this.mgr.el.dom){
34881             this.mgr.el.dom.appendChild(el.dom);
34882         }
34883         if(panel.setRegion){
34884             panel.setRegion(this);
34885         }
34886         this.panels.add(panel);
34887         el.setStyle("position", "absolute");
34888         if(!panel.background){
34889             this.setActivePanel(panel);
34890             if(this.config.initialSize && this.panels.getCount()==1){
34891                 this.resizeTo(this.config.initialSize);
34892             }
34893         }
34894         this.fireEvent("paneladded", this, panel);
34895         return panel;
34896     },
34897     
34898     /**
34899      * Returns true if the panel is in this region.
34900      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34901      * @return {Boolean}
34902      */
34903     hasPanel : function(panel){
34904         if(typeof panel == "object"){ // must be panel obj
34905             panel = panel.getId();
34906         }
34907         return this.getPanel(panel) ? true : false;
34908     },
34909     
34910     /**
34911      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34913      * @param {Boolean} preservePanel Overrides the config preservePanel option
34914      * @return {Roo.ContentPanel} The panel that was removed
34915      */
34916     remove : function(panel, preservePanel){
34917         panel = this.getPanel(panel);
34918         if(!panel){
34919             return null;
34920         }
34921         var e = {};
34922         this.fireEvent("beforeremove", this, panel, e);
34923         if(e.cancel === true){
34924             return null;
34925         }
34926         var panelId = panel.getId();
34927         this.panels.removeKey(panelId);
34928         return panel;
34929     },
34930     
34931     /**
34932      * Returns the panel specified or null if it's not in this region.
34933      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34934      * @return {Roo.ContentPanel}
34935      */
34936     getPanel : function(id){
34937         if(typeof id == "object"){ // must be panel obj
34938             return id;
34939         }
34940         return this.panels.get(id);
34941     },
34942     
34943     /**
34944      * Returns this regions position (north/south/east/west/center).
34945      * @return {String} 
34946      */
34947     getPosition: function(){
34948         return this.position;    
34949     }
34950 });/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960  
34961 /**
34962  * @class Roo.bootstrap.layout.Region
34963  * @extends Roo.bootstrap.layout.Basic
34964  * This class represents a region in a layout manager.
34965  
34966  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34967  * @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})
34968  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34969  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34970  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34971  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34972  * @cfg {String}    title           The title for the region (overrides panel titles)
34973  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34974  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34975  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34976  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34977  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34978  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34979  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34980  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34981  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34982  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34983
34984  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34985  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34986  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34987  * @cfg {Number}    width           For East/West panels
34988  * @cfg {Number}    height          For North/South panels
34989  * @cfg {Boolean}   split           To show the splitter
34990  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34991  * 
34992  * @cfg {string}   cls             Extra CSS classes to add to region
34993  * 
34994  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34995  * @cfg {string}   region  the region that it inhabits..
34996  *
34997
34998  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34999  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35000
35001  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35002  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35003  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35004  */
35005 Roo.bootstrap.layout.Region = function(config)
35006 {
35007     this.applyConfig(config);
35008
35009     var mgr = config.mgr;
35010     var pos = config.region;
35011     config.skipConfig = true;
35012     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35013     
35014     if (mgr.el) {
35015         this.onRender(mgr.el);   
35016     }
35017      
35018     this.visible = true;
35019     this.collapsed = false;
35020     this.unrendered_panels = [];
35021 };
35022
35023 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35024
35025     position: '', // set by wrapper (eg. north/south etc..)
35026     unrendered_panels : null,  // unrendered panels.
35027     createBody : function(){
35028         /** This region's body element 
35029         * @type Roo.Element */
35030         this.bodyEl = this.el.createChild({
35031                 tag: "div",
35032                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35033         });
35034     },
35035
35036     onRender: function(ctr, pos)
35037     {
35038         var dh = Roo.DomHelper;
35039         /** This region's container element 
35040         * @type Roo.Element */
35041         this.el = dh.append(ctr.dom, {
35042                 tag: "div",
35043                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35044             }, true);
35045         /** This region's title element 
35046         * @type Roo.Element */
35047     
35048         this.titleEl = dh.append(this.el.dom,
35049             {
35050                     tag: "div",
35051                     unselectable: "on",
35052                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35053                     children:[
35054                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35055                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35056                     ]}, true);
35057         
35058         this.titleEl.enableDisplayMode();
35059         /** This region's title text element 
35060         * @type HTMLElement */
35061         this.titleTextEl = this.titleEl.dom.firstChild;
35062         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35063         /*
35064         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35065         this.closeBtn.enableDisplayMode();
35066         this.closeBtn.on("click", this.closeClicked, this);
35067         this.closeBtn.hide();
35068     */
35069         this.createBody(this.config);
35070         if(this.config.hideWhenEmpty){
35071             this.hide();
35072             this.on("paneladded", this.validateVisibility, this);
35073             this.on("panelremoved", this.validateVisibility, this);
35074         }
35075         if(this.autoScroll){
35076             this.bodyEl.setStyle("overflow", "auto");
35077         }else{
35078             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35079         }
35080         //if(c.titlebar !== false){
35081             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35082                 this.titleEl.hide();
35083             }else{
35084                 this.titleEl.show();
35085                 if(this.config.title){
35086                     this.titleTextEl.innerHTML = this.config.title;
35087                 }
35088             }
35089         //}
35090         if(this.config.collapsed){
35091             this.collapse(true);
35092         }
35093         if(this.config.hidden){
35094             this.hide();
35095         }
35096         
35097         if (this.unrendered_panels && this.unrendered_panels.length) {
35098             for (var i =0;i< this.unrendered_panels.length; i++) {
35099                 this.add(this.unrendered_panels[i]);
35100             }
35101             this.unrendered_panels = null;
35102             
35103         }
35104         
35105     },
35106     
35107     applyConfig : function(c)
35108     {
35109         /*
35110          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35111             var dh = Roo.DomHelper;
35112             if(c.titlebar !== false){
35113                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35114                 this.collapseBtn.on("click", this.collapse, this);
35115                 this.collapseBtn.enableDisplayMode();
35116                 /*
35117                 if(c.showPin === true || this.showPin){
35118                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35119                     this.stickBtn.enableDisplayMode();
35120                     this.stickBtn.on("click", this.expand, this);
35121                     this.stickBtn.hide();
35122                 }
35123                 
35124             }
35125             */
35126             /** This region's collapsed element
35127             * @type Roo.Element */
35128             /*
35129              *
35130             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35131                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35132             ]}, true);
35133             
35134             if(c.floatable !== false){
35135                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35136                this.collapsedEl.on("click", this.collapseClick, this);
35137             }
35138
35139             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35140                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35141                    id: "message", unselectable: "on", style:{"float":"left"}});
35142                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35143              }
35144             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35145             this.expandBtn.on("click", this.expand, this);
35146             
35147         }
35148         
35149         if(this.collapseBtn){
35150             this.collapseBtn.setVisible(c.collapsible == true);
35151         }
35152         
35153         this.cmargins = c.cmargins || this.cmargins ||
35154                          (this.position == "west" || this.position == "east" ?
35155                              {top: 0, left: 2, right:2, bottom: 0} :
35156                              {top: 2, left: 0, right:0, bottom: 2});
35157         */
35158         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35159         
35160         
35161         this.bottomTabs = c.tabPosition != "top";
35162         
35163         this.autoScroll = c.autoScroll || false;
35164         
35165         
35166        
35167         
35168         this.duration = c.duration || .30;
35169         this.slideDuration = c.slideDuration || .45;
35170         this.config = c;
35171        
35172     },
35173     /**
35174      * Returns true if this region is currently visible.
35175      * @return {Boolean}
35176      */
35177     isVisible : function(){
35178         return this.visible;
35179     },
35180
35181     /**
35182      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35183      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35184      */
35185     //setCollapsedTitle : function(title){
35186     //    title = title || "&#160;";
35187      //   if(this.collapsedTitleTextEl){
35188       //      this.collapsedTitleTextEl.innerHTML = title;
35189        // }
35190     //},
35191
35192     getBox : function(){
35193         var b;
35194       //  if(!this.collapsed){
35195             b = this.el.getBox(false, true);
35196        // }else{
35197           //  b = this.collapsedEl.getBox(false, true);
35198         //}
35199         return b;
35200     },
35201
35202     getMargins : function(){
35203         return this.margins;
35204         //return this.collapsed ? this.cmargins : this.margins;
35205     },
35206 /*
35207     highlight : function(){
35208         this.el.addClass("x-layout-panel-dragover");
35209     },
35210
35211     unhighlight : function(){
35212         this.el.removeClass("x-layout-panel-dragover");
35213     },
35214 */
35215     updateBox : function(box)
35216     {
35217         if (!this.bodyEl) {
35218             return; // not rendered yet..
35219         }
35220         
35221         this.box = box;
35222         if(!this.collapsed){
35223             this.el.dom.style.left = box.x + "px";
35224             this.el.dom.style.top = box.y + "px";
35225             this.updateBody(box.width, box.height);
35226         }else{
35227             this.collapsedEl.dom.style.left = box.x + "px";
35228             this.collapsedEl.dom.style.top = box.y + "px";
35229             this.collapsedEl.setSize(box.width, box.height);
35230         }
35231         if(this.tabs){
35232             this.tabs.autoSizeTabs();
35233         }
35234     },
35235
35236     updateBody : function(w, h)
35237     {
35238         if(w !== null){
35239             this.el.setWidth(w);
35240             w -= this.el.getBorderWidth("rl");
35241             if(this.config.adjustments){
35242                 w += this.config.adjustments[0];
35243             }
35244         }
35245         if(h !== null && h > 0){
35246             this.el.setHeight(h);
35247             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35248             h -= this.el.getBorderWidth("tb");
35249             if(this.config.adjustments){
35250                 h += this.config.adjustments[1];
35251             }
35252             this.bodyEl.setHeight(h);
35253             if(this.tabs){
35254                 h = this.tabs.syncHeight(h);
35255             }
35256         }
35257         if(this.panelSize){
35258             w = w !== null ? w : this.panelSize.width;
35259             h = h !== null ? h : this.panelSize.height;
35260         }
35261         if(this.activePanel){
35262             var el = this.activePanel.getEl();
35263             w = w !== null ? w : el.getWidth();
35264             h = h !== null ? h : el.getHeight();
35265             this.panelSize = {width: w, height: h};
35266             this.activePanel.setSize(w, h);
35267         }
35268         if(Roo.isIE && this.tabs){
35269             this.tabs.el.repaint();
35270         }
35271     },
35272
35273     /**
35274      * Returns the container element for this region.
35275      * @return {Roo.Element}
35276      */
35277     getEl : function(){
35278         return this.el;
35279     },
35280
35281     /**
35282      * Hides this region.
35283      */
35284     hide : function(){
35285         //if(!this.collapsed){
35286             this.el.dom.style.left = "-2000px";
35287             this.el.hide();
35288         //}else{
35289          //   this.collapsedEl.dom.style.left = "-2000px";
35290          //   this.collapsedEl.hide();
35291        // }
35292         this.visible = false;
35293         this.fireEvent("visibilitychange", this, false);
35294     },
35295
35296     /**
35297      * Shows this region if it was previously hidden.
35298      */
35299     show : function(){
35300         //if(!this.collapsed){
35301             this.el.show();
35302         //}else{
35303         //    this.collapsedEl.show();
35304        // }
35305         this.visible = true;
35306         this.fireEvent("visibilitychange", this, true);
35307     },
35308 /*
35309     closeClicked : function(){
35310         if(this.activePanel){
35311             this.remove(this.activePanel);
35312         }
35313     },
35314
35315     collapseClick : function(e){
35316         if(this.isSlid){
35317            e.stopPropagation();
35318            this.slideIn();
35319         }else{
35320            e.stopPropagation();
35321            this.slideOut();
35322         }
35323     },
35324 */
35325     /**
35326      * Collapses this region.
35327      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35328      */
35329     /*
35330     collapse : function(skipAnim, skipCheck = false){
35331         if(this.collapsed) {
35332             return;
35333         }
35334         
35335         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35336             
35337             this.collapsed = true;
35338             if(this.split){
35339                 this.split.el.hide();
35340             }
35341             if(this.config.animate && skipAnim !== true){
35342                 this.fireEvent("invalidated", this);
35343                 this.animateCollapse();
35344             }else{
35345                 this.el.setLocation(-20000,-20000);
35346                 this.el.hide();
35347                 this.collapsedEl.show();
35348                 this.fireEvent("collapsed", this);
35349                 this.fireEvent("invalidated", this);
35350             }
35351         }
35352         
35353     },
35354 */
35355     animateCollapse : function(){
35356         // overridden
35357     },
35358
35359     /**
35360      * Expands this region if it was previously collapsed.
35361      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35362      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35363      */
35364     /*
35365     expand : function(e, skipAnim){
35366         if(e) {
35367             e.stopPropagation();
35368         }
35369         if(!this.collapsed || this.el.hasActiveFx()) {
35370             return;
35371         }
35372         if(this.isSlid){
35373             this.afterSlideIn();
35374             skipAnim = true;
35375         }
35376         this.collapsed = false;
35377         if(this.config.animate && skipAnim !== true){
35378             this.animateExpand();
35379         }else{
35380             this.el.show();
35381             if(this.split){
35382                 this.split.el.show();
35383             }
35384             this.collapsedEl.setLocation(-2000,-2000);
35385             this.collapsedEl.hide();
35386             this.fireEvent("invalidated", this);
35387             this.fireEvent("expanded", this);
35388         }
35389     },
35390 */
35391     animateExpand : function(){
35392         // overridden
35393     },
35394
35395     initTabs : function()
35396     {
35397         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35398         
35399         var ts = new Roo.bootstrap.panel.Tabs({
35400                 el: this.bodyEl.dom,
35401                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35402                 disableTooltips: this.config.disableTabTips,
35403                 toolbar : this.config.toolbar
35404             });
35405         
35406         if(this.config.hideTabs){
35407             ts.stripWrap.setDisplayed(false);
35408         }
35409         this.tabs = ts;
35410         ts.resizeTabs = this.config.resizeTabs === true;
35411         ts.minTabWidth = this.config.minTabWidth || 40;
35412         ts.maxTabWidth = this.config.maxTabWidth || 250;
35413         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35414         ts.monitorResize = false;
35415         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35416         ts.bodyEl.addClass('roo-layout-tabs-body');
35417         this.panels.each(this.initPanelAsTab, this);
35418     },
35419
35420     initPanelAsTab : function(panel){
35421         var ti = this.tabs.addTab(
35422             panel.getEl().id,
35423             panel.getTitle(),
35424             null,
35425             this.config.closeOnTab && panel.isClosable(),
35426             panel.tpl
35427         );
35428         if(panel.tabTip !== undefined){
35429             ti.setTooltip(panel.tabTip);
35430         }
35431         ti.on("activate", function(){
35432               this.setActivePanel(panel);
35433         }, this);
35434         
35435         if(this.config.closeOnTab){
35436             ti.on("beforeclose", function(t, e){
35437                 e.cancel = true;
35438                 this.remove(panel);
35439             }, this);
35440         }
35441         
35442         panel.tabItem = ti;
35443         
35444         return ti;
35445     },
35446
35447     updatePanelTitle : function(panel, title)
35448     {
35449         if(this.activePanel == panel){
35450             this.updateTitle(title);
35451         }
35452         if(this.tabs){
35453             var ti = this.tabs.getTab(panel.getEl().id);
35454             ti.setText(title);
35455             if(panel.tabTip !== undefined){
35456                 ti.setTooltip(panel.tabTip);
35457             }
35458         }
35459     },
35460
35461     updateTitle : function(title){
35462         if(this.titleTextEl && !this.config.title){
35463             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35464         }
35465     },
35466
35467     setActivePanel : function(panel)
35468     {
35469         panel = this.getPanel(panel);
35470         if(this.activePanel && this.activePanel != panel){
35471             if(this.activePanel.setActiveState(false) === false){
35472                 return;
35473             }
35474         }
35475         this.activePanel = panel;
35476         panel.setActiveState(true);
35477         if(this.panelSize){
35478             panel.setSize(this.panelSize.width, this.panelSize.height);
35479         }
35480         if(this.closeBtn){
35481             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35482         }
35483         this.updateTitle(panel.getTitle());
35484         if(this.tabs){
35485             this.fireEvent("invalidated", this);
35486         }
35487         this.fireEvent("panelactivated", this, panel);
35488     },
35489
35490     /**
35491      * Shows the specified panel.
35492      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35493      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35494      */
35495     showPanel : function(panel)
35496     {
35497         panel = this.getPanel(panel);
35498         if(panel){
35499             if(this.tabs){
35500                 var tab = this.tabs.getTab(panel.getEl().id);
35501                 if(tab.isHidden()){
35502                     this.tabs.unhideTab(tab.id);
35503                 }
35504                 tab.activate();
35505             }else{
35506                 this.setActivePanel(panel);
35507             }
35508         }
35509         return panel;
35510     },
35511
35512     /**
35513      * Get the active panel for this region.
35514      * @return {Roo.ContentPanel} The active panel or null
35515      */
35516     getActivePanel : function(){
35517         return this.activePanel;
35518     },
35519
35520     validateVisibility : function(){
35521         if(this.panels.getCount() < 1){
35522             this.updateTitle("&#160;");
35523             this.closeBtn.hide();
35524             this.hide();
35525         }else{
35526             if(!this.isVisible()){
35527                 this.show();
35528             }
35529         }
35530     },
35531
35532     /**
35533      * Adds the passed ContentPanel(s) to this region.
35534      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35535      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35536      */
35537     add : function(panel)
35538     {
35539         if(arguments.length > 1){
35540             for(var i = 0, len = arguments.length; i < len; i++) {
35541                 this.add(arguments[i]);
35542             }
35543             return null;
35544         }
35545         
35546         // if we have not been rendered yet, then we can not really do much of this..
35547         if (!this.bodyEl) {
35548             this.unrendered_panels.push(panel);
35549             return panel;
35550         }
35551         
35552         
35553         
35554         
35555         if(this.hasPanel(panel)){
35556             this.showPanel(panel);
35557             return panel;
35558         }
35559         panel.setRegion(this);
35560         this.panels.add(panel);
35561        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35562             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35563             // and hide them... ???
35564             this.bodyEl.dom.appendChild(panel.getEl().dom);
35565             if(panel.background !== true){
35566                 this.setActivePanel(panel);
35567             }
35568             this.fireEvent("paneladded", this, panel);
35569             return panel;
35570         }
35571         */
35572         if(!this.tabs){
35573             this.initTabs();
35574         }else{
35575             this.initPanelAsTab(panel);
35576         }
35577         
35578         
35579         if(panel.background !== true){
35580             this.tabs.activate(panel.getEl().id);
35581         }
35582         this.fireEvent("paneladded", this, panel);
35583         return panel;
35584     },
35585
35586     /**
35587      * Hides the tab for the specified panel.
35588      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35589      */
35590     hidePanel : function(panel){
35591         if(this.tabs && (panel = this.getPanel(panel))){
35592             this.tabs.hideTab(panel.getEl().id);
35593         }
35594     },
35595
35596     /**
35597      * Unhides the tab for a previously hidden panel.
35598      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35599      */
35600     unhidePanel : function(panel){
35601         if(this.tabs && (panel = this.getPanel(panel))){
35602             this.tabs.unhideTab(panel.getEl().id);
35603         }
35604     },
35605
35606     clearPanels : function(){
35607         while(this.panels.getCount() > 0){
35608              this.remove(this.panels.first());
35609         }
35610     },
35611
35612     /**
35613      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35614      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35615      * @param {Boolean} preservePanel Overrides the config preservePanel option
35616      * @return {Roo.ContentPanel} The panel that was removed
35617      */
35618     remove : function(panel, preservePanel)
35619     {
35620         panel = this.getPanel(panel);
35621         if(!panel){
35622             return null;
35623         }
35624         var e = {};
35625         this.fireEvent("beforeremove", this, panel, e);
35626         if(e.cancel === true){
35627             return null;
35628         }
35629         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35630         var panelId = panel.getId();
35631         this.panels.removeKey(panelId);
35632         if(preservePanel){
35633             document.body.appendChild(panel.getEl().dom);
35634         }
35635         if(this.tabs){
35636             this.tabs.removeTab(panel.getEl().id);
35637         }else if (!preservePanel){
35638             this.bodyEl.dom.removeChild(panel.getEl().dom);
35639         }
35640         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35641             var p = this.panels.first();
35642             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35643             tempEl.appendChild(p.getEl().dom);
35644             this.bodyEl.update("");
35645             this.bodyEl.dom.appendChild(p.getEl().dom);
35646             tempEl = null;
35647             this.updateTitle(p.getTitle());
35648             this.tabs = null;
35649             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35650             this.setActivePanel(p);
35651         }
35652         panel.setRegion(null);
35653         if(this.activePanel == panel){
35654             this.activePanel = null;
35655         }
35656         if(this.config.autoDestroy !== false && preservePanel !== true){
35657             try{panel.destroy();}catch(e){}
35658         }
35659         this.fireEvent("panelremoved", this, panel);
35660         return panel;
35661     },
35662
35663     /**
35664      * Returns the TabPanel component used by this region
35665      * @return {Roo.TabPanel}
35666      */
35667     getTabs : function(){
35668         return this.tabs;
35669     },
35670
35671     createTool : function(parentEl, className){
35672         var btn = Roo.DomHelper.append(parentEl, {
35673             tag: "div",
35674             cls: "x-layout-tools-button",
35675             children: [ {
35676                 tag: "div",
35677                 cls: "roo-layout-tools-button-inner " + className,
35678                 html: "&#160;"
35679             }]
35680         }, true);
35681         btn.addClassOnOver("roo-layout-tools-button-over");
35682         return btn;
35683     }
35684 });/*
35685  * Based on:
35686  * Ext JS Library 1.1.1
35687  * Copyright(c) 2006-2007, Ext JS, LLC.
35688  *
35689  * Originally Released Under LGPL - original licence link has changed is not relivant.
35690  *
35691  * Fork - LGPL
35692  * <script type="text/javascript">
35693  */
35694  
35695
35696
35697 /**
35698  * @class Roo.SplitLayoutRegion
35699  * @extends Roo.LayoutRegion
35700  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35701  */
35702 Roo.bootstrap.layout.Split = function(config){
35703     this.cursor = config.cursor;
35704     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35705 };
35706
35707 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35708 {
35709     splitTip : "Drag to resize.",
35710     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35711     useSplitTips : false,
35712
35713     applyConfig : function(config){
35714         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35715     },
35716     
35717     onRender : function(ctr,pos) {
35718         
35719         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35720         if(!this.config.split){
35721             return;
35722         }
35723         if(!this.split){
35724             
35725             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35726                             tag: "div",
35727                             id: this.el.id + "-split",
35728                             cls: "roo-layout-split roo-layout-split-"+this.position,
35729                             html: "&#160;"
35730             });
35731             /** The SplitBar for this region 
35732             * @type Roo.SplitBar */
35733             // does not exist yet...
35734             Roo.log([this.position, this.orientation]);
35735             
35736             this.split = new Roo.bootstrap.SplitBar({
35737                 dragElement : splitEl,
35738                 resizingElement: this.el,
35739                 orientation : this.orientation
35740             });
35741             
35742             this.split.on("moved", this.onSplitMove, this);
35743             this.split.useShim = this.config.useShim === true;
35744             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35745             if(this.useSplitTips){
35746                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35747             }
35748             //if(config.collapsible){
35749             //    this.split.el.on("dblclick", this.collapse,  this);
35750             //}
35751         }
35752         if(typeof this.config.minSize != "undefined"){
35753             this.split.minSize = this.config.minSize;
35754         }
35755         if(typeof this.config.maxSize != "undefined"){
35756             this.split.maxSize = this.config.maxSize;
35757         }
35758         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35759             this.hideSplitter();
35760         }
35761         
35762     },
35763
35764     getHMaxSize : function(){
35765          var cmax = this.config.maxSize || 10000;
35766          var center = this.mgr.getRegion("center");
35767          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35768     },
35769
35770     getVMaxSize : function(){
35771          var cmax = this.config.maxSize || 10000;
35772          var center = this.mgr.getRegion("center");
35773          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35774     },
35775
35776     onSplitMove : function(split, newSize){
35777         this.fireEvent("resized", this, newSize);
35778     },
35779     
35780     /** 
35781      * Returns the {@link Roo.SplitBar} for this region.
35782      * @return {Roo.SplitBar}
35783      */
35784     getSplitBar : function(){
35785         return this.split;
35786     },
35787     
35788     hide : function(){
35789         this.hideSplitter();
35790         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35791     },
35792
35793     hideSplitter : function(){
35794         if(this.split){
35795             this.split.el.setLocation(-2000,-2000);
35796             this.split.el.hide();
35797         }
35798     },
35799
35800     show : function(){
35801         if(this.split){
35802             this.split.el.show();
35803         }
35804         Roo.bootstrap.layout.Split.superclass.show.call(this);
35805     },
35806     
35807     beforeSlide: function(){
35808         if(Roo.isGecko){// firefox overflow auto bug workaround
35809             this.bodyEl.clip();
35810             if(this.tabs) {
35811                 this.tabs.bodyEl.clip();
35812             }
35813             if(this.activePanel){
35814                 this.activePanel.getEl().clip();
35815                 
35816                 if(this.activePanel.beforeSlide){
35817                     this.activePanel.beforeSlide();
35818                 }
35819             }
35820         }
35821     },
35822     
35823     afterSlide : function(){
35824         if(Roo.isGecko){// firefox overflow auto bug workaround
35825             this.bodyEl.unclip();
35826             if(this.tabs) {
35827                 this.tabs.bodyEl.unclip();
35828             }
35829             if(this.activePanel){
35830                 this.activePanel.getEl().unclip();
35831                 if(this.activePanel.afterSlide){
35832                     this.activePanel.afterSlide();
35833                 }
35834             }
35835         }
35836     },
35837
35838     initAutoHide : function(){
35839         if(this.autoHide !== false){
35840             if(!this.autoHideHd){
35841                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35842                 this.autoHideHd = {
35843                     "mouseout": function(e){
35844                         if(!e.within(this.el, true)){
35845                             st.delay(500);
35846                         }
35847                     },
35848                     "mouseover" : function(e){
35849                         st.cancel();
35850                     },
35851                     scope : this
35852                 };
35853             }
35854             this.el.on(this.autoHideHd);
35855         }
35856     },
35857
35858     clearAutoHide : function(){
35859         if(this.autoHide !== false){
35860             this.el.un("mouseout", this.autoHideHd.mouseout);
35861             this.el.un("mouseover", this.autoHideHd.mouseover);
35862         }
35863     },
35864
35865     clearMonitor : function(){
35866         Roo.get(document).un("click", this.slideInIf, this);
35867     },
35868
35869     // these names are backwards but not changed for compat
35870     slideOut : function(){
35871         if(this.isSlid || this.el.hasActiveFx()){
35872             return;
35873         }
35874         this.isSlid = true;
35875         if(this.collapseBtn){
35876             this.collapseBtn.hide();
35877         }
35878         this.closeBtnState = this.closeBtn.getStyle('display');
35879         this.closeBtn.hide();
35880         if(this.stickBtn){
35881             this.stickBtn.show();
35882         }
35883         this.el.show();
35884         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35885         this.beforeSlide();
35886         this.el.setStyle("z-index", 10001);
35887         this.el.slideIn(this.getSlideAnchor(), {
35888             callback: function(){
35889                 this.afterSlide();
35890                 this.initAutoHide();
35891                 Roo.get(document).on("click", this.slideInIf, this);
35892                 this.fireEvent("slideshow", this);
35893             },
35894             scope: this,
35895             block: true
35896         });
35897     },
35898
35899     afterSlideIn : function(){
35900         this.clearAutoHide();
35901         this.isSlid = false;
35902         this.clearMonitor();
35903         this.el.setStyle("z-index", "");
35904         if(this.collapseBtn){
35905             this.collapseBtn.show();
35906         }
35907         this.closeBtn.setStyle('display', this.closeBtnState);
35908         if(this.stickBtn){
35909             this.stickBtn.hide();
35910         }
35911         this.fireEvent("slidehide", this);
35912     },
35913
35914     slideIn : function(cb){
35915         if(!this.isSlid || this.el.hasActiveFx()){
35916             Roo.callback(cb);
35917             return;
35918         }
35919         this.isSlid = false;
35920         this.beforeSlide();
35921         this.el.slideOut(this.getSlideAnchor(), {
35922             callback: function(){
35923                 this.el.setLeftTop(-10000, -10000);
35924                 this.afterSlide();
35925                 this.afterSlideIn();
35926                 Roo.callback(cb);
35927             },
35928             scope: this,
35929             block: true
35930         });
35931     },
35932     
35933     slideInIf : function(e){
35934         if(!e.within(this.el)){
35935             this.slideIn();
35936         }
35937     },
35938
35939     animateCollapse : function(){
35940         this.beforeSlide();
35941         this.el.setStyle("z-index", 20000);
35942         var anchor = this.getSlideAnchor();
35943         this.el.slideOut(anchor, {
35944             callback : function(){
35945                 this.el.setStyle("z-index", "");
35946                 this.collapsedEl.slideIn(anchor, {duration:.3});
35947                 this.afterSlide();
35948                 this.el.setLocation(-10000,-10000);
35949                 this.el.hide();
35950                 this.fireEvent("collapsed", this);
35951             },
35952             scope: this,
35953             block: true
35954         });
35955     },
35956
35957     animateExpand : function(){
35958         this.beforeSlide();
35959         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35960         this.el.setStyle("z-index", 20000);
35961         this.collapsedEl.hide({
35962             duration:.1
35963         });
35964         this.el.slideIn(this.getSlideAnchor(), {
35965             callback : function(){
35966                 this.el.setStyle("z-index", "");
35967                 this.afterSlide();
35968                 if(this.split){
35969                     this.split.el.show();
35970                 }
35971                 this.fireEvent("invalidated", this);
35972                 this.fireEvent("expanded", this);
35973             },
35974             scope: this,
35975             block: true
35976         });
35977     },
35978
35979     anchors : {
35980         "west" : "left",
35981         "east" : "right",
35982         "north" : "top",
35983         "south" : "bottom"
35984     },
35985
35986     sanchors : {
35987         "west" : "l",
35988         "east" : "r",
35989         "north" : "t",
35990         "south" : "b"
35991     },
35992
35993     canchors : {
35994         "west" : "tl-tr",
35995         "east" : "tr-tl",
35996         "north" : "tl-bl",
35997         "south" : "bl-tl"
35998     },
35999
36000     getAnchor : function(){
36001         return this.anchors[this.position];
36002     },
36003
36004     getCollapseAnchor : function(){
36005         return this.canchors[this.position];
36006     },
36007
36008     getSlideAnchor : function(){
36009         return this.sanchors[this.position];
36010     },
36011
36012     getAlignAdj : function(){
36013         var cm = this.cmargins;
36014         switch(this.position){
36015             case "west":
36016                 return [0, 0];
36017             break;
36018             case "east":
36019                 return [0, 0];
36020             break;
36021             case "north":
36022                 return [0, 0];
36023             break;
36024             case "south":
36025                 return [0, 0];
36026             break;
36027         }
36028     },
36029
36030     getExpandAdj : function(){
36031         var c = this.collapsedEl, cm = this.cmargins;
36032         switch(this.position){
36033             case "west":
36034                 return [-(cm.right+c.getWidth()+cm.left), 0];
36035             break;
36036             case "east":
36037                 return [cm.right+c.getWidth()+cm.left, 0];
36038             break;
36039             case "north":
36040                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36041             break;
36042             case "south":
36043                 return [0, cm.top+cm.bottom+c.getHeight()];
36044             break;
36045         }
36046     }
36047 });/*
36048  * Based on:
36049  * Ext JS Library 1.1.1
36050  * Copyright(c) 2006-2007, Ext JS, LLC.
36051  *
36052  * Originally Released Under LGPL - original licence link has changed is not relivant.
36053  *
36054  * Fork - LGPL
36055  * <script type="text/javascript">
36056  */
36057 /*
36058  * These classes are private internal classes
36059  */
36060 Roo.bootstrap.layout.Center = function(config){
36061     config.region = "center";
36062     Roo.bootstrap.layout.Region.call(this, config);
36063     this.visible = true;
36064     this.minWidth = config.minWidth || 20;
36065     this.minHeight = config.minHeight || 20;
36066 };
36067
36068 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36069     hide : function(){
36070         // center panel can't be hidden
36071     },
36072     
36073     show : function(){
36074         // center panel can't be hidden
36075     },
36076     
36077     getMinWidth: function(){
36078         return this.minWidth;
36079     },
36080     
36081     getMinHeight: function(){
36082         return this.minHeight;
36083     }
36084 });
36085
36086
36087
36088
36089  
36090
36091
36092
36093
36094
36095 Roo.bootstrap.layout.North = function(config)
36096 {
36097     config.region = 'north';
36098     config.cursor = 'n-resize';
36099     
36100     Roo.bootstrap.layout.Split.call(this, config);
36101     
36102     
36103     if(this.split){
36104         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36105         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36106         this.split.el.addClass("roo-layout-split-v");
36107     }
36108     var size = config.initialSize || config.height;
36109     if(typeof size != "undefined"){
36110         this.el.setHeight(size);
36111     }
36112 };
36113 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36114 {
36115     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36116     
36117     
36118     
36119     getBox : function(){
36120         if(this.collapsed){
36121             return this.collapsedEl.getBox();
36122         }
36123         var box = this.el.getBox();
36124         if(this.split){
36125             box.height += this.split.el.getHeight();
36126         }
36127         return box;
36128     },
36129     
36130     updateBox : function(box){
36131         if(this.split && !this.collapsed){
36132             box.height -= this.split.el.getHeight();
36133             this.split.el.setLeft(box.x);
36134             this.split.el.setTop(box.y+box.height);
36135             this.split.el.setWidth(box.width);
36136         }
36137         if(this.collapsed){
36138             this.updateBody(box.width, null);
36139         }
36140         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36141     }
36142 });
36143
36144
36145
36146
36147
36148 Roo.bootstrap.layout.South = function(config){
36149     config.region = 'south';
36150     config.cursor = 's-resize';
36151     Roo.bootstrap.layout.Split.call(this, config);
36152     if(this.split){
36153         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36154         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36155         this.split.el.addClass("roo-layout-split-v");
36156     }
36157     var size = config.initialSize || config.height;
36158     if(typeof size != "undefined"){
36159         this.el.setHeight(size);
36160     }
36161 };
36162
36163 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36164     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36165     getBox : function(){
36166         if(this.collapsed){
36167             return this.collapsedEl.getBox();
36168         }
36169         var box = this.el.getBox();
36170         if(this.split){
36171             var sh = this.split.el.getHeight();
36172             box.height += sh;
36173             box.y -= sh;
36174         }
36175         return box;
36176     },
36177     
36178     updateBox : function(box){
36179         if(this.split && !this.collapsed){
36180             var sh = this.split.el.getHeight();
36181             box.height -= sh;
36182             box.y += sh;
36183             this.split.el.setLeft(box.x);
36184             this.split.el.setTop(box.y-sh);
36185             this.split.el.setWidth(box.width);
36186         }
36187         if(this.collapsed){
36188             this.updateBody(box.width, null);
36189         }
36190         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36191     }
36192 });
36193
36194 Roo.bootstrap.layout.East = function(config){
36195     config.region = "east";
36196     config.cursor = "e-resize";
36197     Roo.bootstrap.layout.Split.call(this, config);
36198     if(this.split){
36199         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36200         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36201         this.split.el.addClass("roo-layout-split-h");
36202     }
36203     var size = config.initialSize || config.width;
36204     if(typeof size != "undefined"){
36205         this.el.setWidth(size);
36206     }
36207 };
36208 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36209     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36210     getBox : function(){
36211         if(this.collapsed){
36212             return this.collapsedEl.getBox();
36213         }
36214         var box = this.el.getBox();
36215         if(this.split){
36216             var sw = this.split.el.getWidth();
36217             box.width += sw;
36218             box.x -= sw;
36219         }
36220         return box;
36221     },
36222
36223     updateBox : function(box){
36224         if(this.split && !this.collapsed){
36225             var sw = this.split.el.getWidth();
36226             box.width -= sw;
36227             this.split.el.setLeft(box.x);
36228             this.split.el.setTop(box.y);
36229             this.split.el.setHeight(box.height);
36230             box.x += sw;
36231         }
36232         if(this.collapsed){
36233             this.updateBody(null, box.height);
36234         }
36235         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36236     }
36237 });
36238
36239 Roo.bootstrap.layout.West = function(config){
36240     config.region = "west";
36241     config.cursor = "w-resize";
36242     
36243     Roo.bootstrap.layout.Split.call(this, config);
36244     if(this.split){
36245         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36246         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36247         this.split.el.addClass("roo-layout-split-h");
36248     }
36249     
36250 };
36251 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36252     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36253     
36254     onRender: function(ctr, pos)
36255     {
36256         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36257         var size = this.config.initialSize || this.config.width;
36258         if(typeof size != "undefined"){
36259             this.el.setWidth(size);
36260         }
36261     },
36262     
36263     getBox : function(){
36264         if(this.collapsed){
36265             return this.collapsedEl.getBox();
36266         }
36267         var box = this.el.getBox();
36268         if(this.split){
36269             box.width += this.split.el.getWidth();
36270         }
36271         return box;
36272     },
36273     
36274     updateBox : function(box){
36275         if(this.split && !this.collapsed){
36276             var sw = this.split.el.getWidth();
36277             box.width -= sw;
36278             this.split.el.setLeft(box.x+box.width);
36279             this.split.el.setTop(box.y);
36280             this.split.el.setHeight(box.height);
36281         }
36282         if(this.collapsed){
36283             this.updateBody(null, box.height);
36284         }
36285         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36286     }
36287 });
36288 Roo.namespace("Roo.bootstrap.panel");/*
36289  * Based on:
36290  * Ext JS Library 1.1.1
36291  * Copyright(c) 2006-2007, Ext JS, LLC.
36292  *
36293  * Originally Released Under LGPL - original licence link has changed is not relivant.
36294  *
36295  * Fork - LGPL
36296  * <script type="text/javascript">
36297  */
36298 /**
36299  * @class Roo.ContentPanel
36300  * @extends Roo.util.Observable
36301  * A basic ContentPanel element.
36302  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36303  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36304  * @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
36305  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36306  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36307  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36308  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36309  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36310  * @cfg {String} title          The title for this panel
36311  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36312  * @cfg {String} url            Calls {@link #setUrl} with this value
36313  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36314  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36315  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36316  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36317  * @cfg {Boolean} badges render the badges
36318
36319  * @constructor
36320  * Create a new ContentPanel.
36321  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36322  * @param {String/Object} config A string to set only the title or a config object
36323  * @param {String} content (optional) Set the HTML content for this panel
36324  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36325  */
36326 Roo.bootstrap.panel.Content = function( config){
36327     
36328     this.tpl = config.tpl || false;
36329     
36330     var el = config.el;
36331     var content = config.content;
36332
36333     if(config.autoCreate){ // xtype is available if this is called from factory
36334         el = Roo.id();
36335     }
36336     this.el = Roo.get(el);
36337     if(!this.el && config && config.autoCreate){
36338         if(typeof config.autoCreate == "object"){
36339             if(!config.autoCreate.id){
36340                 config.autoCreate.id = config.id||el;
36341             }
36342             this.el = Roo.DomHelper.append(document.body,
36343                         config.autoCreate, true);
36344         }else{
36345             var elcfg =  {   tag: "div",
36346                             cls: "roo-layout-inactive-content",
36347                             id: config.id||el
36348                             };
36349             if (config.html) {
36350                 elcfg.html = config.html;
36351                 
36352             }
36353                         
36354             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36355         }
36356     } 
36357     this.closable = false;
36358     this.loaded = false;
36359     this.active = false;
36360    
36361       
36362     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36363         
36364         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36365         
36366         this.wrapEl = this.el; //this.el.wrap();
36367         var ti = [];
36368         if (config.toolbar.items) {
36369             ti = config.toolbar.items ;
36370             delete config.toolbar.items ;
36371         }
36372         
36373         var nitems = [];
36374         this.toolbar.render(this.wrapEl, 'before');
36375         for(var i =0;i < ti.length;i++) {
36376           //  Roo.log(['add child', items[i]]);
36377             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36378         }
36379         this.toolbar.items = nitems;
36380         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36381         delete config.toolbar;
36382         
36383     }
36384     /*
36385     // xtype created footer. - not sure if will work as we normally have to render first..
36386     if (this.footer && !this.footer.el && this.footer.xtype) {
36387         if (!this.wrapEl) {
36388             this.wrapEl = this.el.wrap();
36389         }
36390     
36391         this.footer.container = this.wrapEl.createChild();
36392          
36393         this.footer = Roo.factory(this.footer, Roo);
36394         
36395     }
36396     */
36397     
36398      if(typeof config == "string"){
36399         this.title = config;
36400     }else{
36401         Roo.apply(this, config);
36402     }
36403     
36404     if(this.resizeEl){
36405         this.resizeEl = Roo.get(this.resizeEl, true);
36406     }else{
36407         this.resizeEl = this.el;
36408     }
36409     // handle view.xtype
36410     
36411  
36412     
36413     
36414     this.addEvents({
36415         /**
36416          * @event activate
36417          * Fires when this panel is activated. 
36418          * @param {Roo.ContentPanel} this
36419          */
36420         "activate" : true,
36421         /**
36422          * @event deactivate
36423          * Fires when this panel is activated. 
36424          * @param {Roo.ContentPanel} this
36425          */
36426         "deactivate" : true,
36427
36428         /**
36429          * @event resize
36430          * Fires when this panel is resized if fitToFrame is true.
36431          * @param {Roo.ContentPanel} this
36432          * @param {Number} width The width after any component adjustments
36433          * @param {Number} height The height after any component adjustments
36434          */
36435         "resize" : true,
36436         
36437          /**
36438          * @event render
36439          * Fires when this tab is created
36440          * @param {Roo.ContentPanel} this
36441          */
36442         "render" : true
36443         
36444         
36445         
36446     });
36447     
36448
36449     
36450     
36451     if(this.autoScroll){
36452         this.resizeEl.setStyle("overflow", "auto");
36453     } else {
36454         // fix randome scrolling
36455         //this.el.on('scroll', function() {
36456         //    Roo.log('fix random scolling');
36457         //    this.scrollTo('top',0); 
36458         //});
36459     }
36460     content = content || this.content;
36461     if(content){
36462         this.setContent(content);
36463     }
36464     if(config && config.url){
36465         this.setUrl(this.url, this.params, this.loadOnce);
36466     }
36467     
36468     
36469     
36470     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36471     
36472     if (this.view && typeof(this.view.xtype) != 'undefined') {
36473         this.view.el = this.el.appendChild(document.createElement("div"));
36474         this.view = Roo.factory(this.view); 
36475         this.view.render  &&  this.view.render(false, '');  
36476     }
36477     
36478     
36479     this.fireEvent('render', this);
36480 };
36481
36482 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36483     
36484     tabTip : '',
36485     
36486     setRegion : function(region){
36487         this.region = region;
36488         this.setActiveClass(region && !this.background);
36489     },
36490     
36491     
36492     setActiveClass: function(state)
36493     {
36494         if(state){
36495            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36496            this.el.setStyle('position','relative');
36497         }else{
36498            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36499            this.el.setStyle('position', 'absolute');
36500         } 
36501     },
36502     
36503     /**
36504      * Returns the toolbar for this Panel if one was configured. 
36505      * @return {Roo.Toolbar} 
36506      */
36507     getToolbar : function(){
36508         return this.toolbar;
36509     },
36510     
36511     setActiveState : function(active)
36512     {
36513         this.active = active;
36514         this.setActiveClass(active);
36515         if(!active){
36516             if(this.fireEvent("deactivate", this) === false){
36517                 return false;
36518             }
36519             return true;
36520         }
36521         this.fireEvent("activate", this);
36522         return true;
36523     },
36524     /**
36525      * Updates this panel's element
36526      * @param {String} content The new content
36527      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36528     */
36529     setContent : function(content, loadScripts){
36530         this.el.update(content, loadScripts);
36531     },
36532
36533     ignoreResize : function(w, h){
36534         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36535             return true;
36536         }else{
36537             this.lastSize = {width: w, height: h};
36538             return false;
36539         }
36540     },
36541     /**
36542      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36543      * @return {Roo.UpdateManager} The UpdateManager
36544      */
36545     getUpdateManager : function(){
36546         return this.el.getUpdateManager();
36547     },
36548      /**
36549      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36550      * @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:
36551 <pre><code>
36552 panel.load({
36553     url: "your-url.php",
36554     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36555     callback: yourFunction,
36556     scope: yourObject, //(optional scope)
36557     discardUrl: false,
36558     nocache: false,
36559     text: "Loading...",
36560     timeout: 30,
36561     scripts: false
36562 });
36563 </code></pre>
36564      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36565      * 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.
36566      * @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}
36567      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36568      * @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.
36569      * @return {Roo.ContentPanel} this
36570      */
36571     load : function(){
36572         var um = this.el.getUpdateManager();
36573         um.update.apply(um, arguments);
36574         return this;
36575     },
36576
36577
36578     /**
36579      * 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.
36580      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36581      * @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)
36582      * @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)
36583      * @return {Roo.UpdateManager} The UpdateManager
36584      */
36585     setUrl : function(url, params, loadOnce){
36586         if(this.refreshDelegate){
36587             this.removeListener("activate", this.refreshDelegate);
36588         }
36589         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36590         this.on("activate", this.refreshDelegate);
36591         return this.el.getUpdateManager();
36592     },
36593     
36594     _handleRefresh : function(url, params, loadOnce){
36595         if(!loadOnce || !this.loaded){
36596             var updater = this.el.getUpdateManager();
36597             updater.update(url, params, this._setLoaded.createDelegate(this));
36598         }
36599     },
36600     
36601     _setLoaded : function(){
36602         this.loaded = true;
36603     }, 
36604     
36605     /**
36606      * Returns this panel's id
36607      * @return {String} 
36608      */
36609     getId : function(){
36610         return this.el.id;
36611     },
36612     
36613     /** 
36614      * Returns this panel's element - used by regiosn to add.
36615      * @return {Roo.Element} 
36616      */
36617     getEl : function(){
36618         return this.wrapEl || this.el;
36619     },
36620     
36621    
36622     
36623     adjustForComponents : function(width, height)
36624     {
36625         //Roo.log('adjustForComponents ');
36626         if(this.resizeEl != this.el){
36627             width -= this.el.getFrameWidth('lr');
36628             height -= this.el.getFrameWidth('tb');
36629         }
36630         if(this.toolbar){
36631             var te = this.toolbar.getEl();
36632             te.setWidth(width);
36633             height -= te.getHeight();
36634         }
36635         if(this.footer){
36636             var te = this.footer.getEl();
36637             te.setWidth(width);
36638             height -= te.getHeight();
36639         }
36640         
36641         
36642         if(this.adjustments){
36643             width += this.adjustments[0];
36644             height += this.adjustments[1];
36645         }
36646         return {"width": width, "height": height};
36647     },
36648     
36649     setSize : function(width, height){
36650         if(this.fitToFrame && !this.ignoreResize(width, height)){
36651             if(this.fitContainer && this.resizeEl != this.el){
36652                 this.el.setSize(width, height);
36653             }
36654             var size = this.adjustForComponents(width, height);
36655             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36656             this.fireEvent('resize', this, size.width, size.height);
36657         }
36658     },
36659     
36660     /**
36661      * Returns this panel's title
36662      * @return {String} 
36663      */
36664     getTitle : function(){
36665         
36666         if (typeof(this.title) != 'object') {
36667             return this.title;
36668         }
36669         
36670         var t = '';
36671         for (var k in this.title) {
36672             if (!this.title.hasOwnProperty(k)) {
36673                 continue;
36674             }
36675             
36676             if (k.indexOf('-') >= 0) {
36677                 var s = k.split('-');
36678                 for (var i = 0; i<s.length; i++) {
36679                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36680                 }
36681             } else {
36682                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36683             }
36684         }
36685         return t;
36686     },
36687     
36688     /**
36689      * Set this panel's title
36690      * @param {String} title
36691      */
36692     setTitle : function(title){
36693         this.title = title;
36694         if(this.region){
36695             this.region.updatePanelTitle(this, title);
36696         }
36697     },
36698     
36699     /**
36700      * Returns true is this panel was configured to be closable
36701      * @return {Boolean} 
36702      */
36703     isClosable : function(){
36704         return this.closable;
36705     },
36706     
36707     beforeSlide : function(){
36708         this.el.clip();
36709         this.resizeEl.clip();
36710     },
36711     
36712     afterSlide : function(){
36713         this.el.unclip();
36714         this.resizeEl.unclip();
36715     },
36716     
36717     /**
36718      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36719      *   Will fail silently if the {@link #setUrl} method has not been called.
36720      *   This does not activate the panel, just updates its content.
36721      */
36722     refresh : function(){
36723         if(this.refreshDelegate){
36724            this.loaded = false;
36725            this.refreshDelegate();
36726         }
36727     },
36728     
36729     /**
36730      * Destroys this panel
36731      */
36732     destroy : function(){
36733         this.el.removeAllListeners();
36734         var tempEl = document.createElement("span");
36735         tempEl.appendChild(this.el.dom);
36736         tempEl.innerHTML = "";
36737         this.el.remove();
36738         this.el = null;
36739     },
36740     
36741     /**
36742      * form - if the content panel contains a form - this is a reference to it.
36743      * @type {Roo.form.Form}
36744      */
36745     form : false,
36746     /**
36747      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36748      *    This contains a reference to it.
36749      * @type {Roo.View}
36750      */
36751     view : false,
36752     
36753       /**
36754      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36755      * <pre><code>
36756
36757 layout.addxtype({
36758        xtype : 'Form',
36759        items: [ .... ]
36760    }
36761 );
36762
36763 </code></pre>
36764      * @param {Object} cfg Xtype definition of item to add.
36765      */
36766     
36767     
36768     getChildContainer: function () {
36769         return this.getEl();
36770     }
36771     
36772     
36773     /*
36774         var  ret = new Roo.factory(cfg);
36775         return ret;
36776         
36777         
36778         // add form..
36779         if (cfg.xtype.match(/^Form$/)) {
36780             
36781             var el;
36782             //if (this.footer) {
36783             //    el = this.footer.container.insertSibling(false, 'before');
36784             //} else {
36785                 el = this.el.createChild();
36786             //}
36787
36788             this.form = new  Roo.form.Form(cfg);
36789             
36790             
36791             if ( this.form.allItems.length) {
36792                 this.form.render(el.dom);
36793             }
36794             return this.form;
36795         }
36796         // should only have one of theses..
36797         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36798             // views.. should not be just added - used named prop 'view''
36799             
36800             cfg.el = this.el.appendChild(document.createElement("div"));
36801             // factory?
36802             
36803             var ret = new Roo.factory(cfg);
36804              
36805              ret.render && ret.render(false, ''); // render blank..
36806             this.view = ret;
36807             return ret;
36808         }
36809         return false;
36810     }
36811     \*/
36812 });
36813  
36814 /**
36815  * @class Roo.bootstrap.panel.Grid
36816  * @extends Roo.bootstrap.panel.Content
36817  * @constructor
36818  * Create a new GridPanel.
36819  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36820  * @param {Object} config A the config object
36821   
36822  */
36823
36824
36825
36826 Roo.bootstrap.panel.Grid = function(config)
36827 {
36828     
36829       
36830     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36831         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36832
36833     config.el = this.wrapper;
36834     //this.el = this.wrapper;
36835     
36836       if (config.container) {
36837         // ctor'ed from a Border/panel.grid
36838         
36839         
36840         this.wrapper.setStyle("overflow", "hidden");
36841         this.wrapper.addClass('roo-grid-container');
36842
36843     }
36844     
36845     
36846     if(config.toolbar){
36847         var tool_el = this.wrapper.createChild();    
36848         this.toolbar = Roo.factory(config.toolbar);
36849         var ti = [];
36850         if (config.toolbar.items) {
36851             ti = config.toolbar.items ;
36852             delete config.toolbar.items ;
36853         }
36854         
36855         var nitems = [];
36856         this.toolbar.render(tool_el);
36857         for(var i =0;i < ti.length;i++) {
36858           //  Roo.log(['add child', items[i]]);
36859             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36860         }
36861         this.toolbar.items = nitems;
36862         
36863         delete config.toolbar;
36864     }
36865     
36866     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36867     config.grid.scrollBody = true;;
36868     config.grid.monitorWindowResize = false; // turn off autosizing
36869     config.grid.autoHeight = false;
36870     config.grid.autoWidth = false;
36871     
36872     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36873     
36874     if (config.background) {
36875         // render grid on panel activation (if panel background)
36876         this.on('activate', function(gp) {
36877             if (!gp.grid.rendered) {
36878                 gp.grid.render(this.wrapper);
36879                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36880             }
36881         });
36882             
36883     } else {
36884         this.grid.render(this.wrapper);
36885         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36886
36887     }
36888     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36889     // ??? needed ??? config.el = this.wrapper;
36890     
36891     
36892     
36893   
36894     // xtype created footer. - not sure if will work as we normally have to render first..
36895     if (this.footer && !this.footer.el && this.footer.xtype) {
36896         
36897         var ctr = this.grid.getView().getFooterPanel(true);
36898         this.footer.dataSource = this.grid.dataSource;
36899         this.footer = Roo.factory(this.footer, Roo);
36900         this.footer.render(ctr);
36901         
36902     }
36903     
36904     
36905     
36906     
36907      
36908 };
36909
36910 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36911     getId : function(){
36912         return this.grid.id;
36913     },
36914     
36915     /**
36916      * Returns the grid for this panel
36917      * @return {Roo.bootstrap.Table} 
36918      */
36919     getGrid : function(){
36920         return this.grid;    
36921     },
36922     
36923     setSize : function(width, height){
36924         if(!this.ignoreResize(width, height)){
36925             var grid = this.grid;
36926             var size = this.adjustForComponents(width, height);
36927             var gridel = grid.getGridEl();
36928             gridel.setSize(size.width, size.height);
36929             /*
36930             var thd = grid.getGridEl().select('thead',true).first();
36931             var tbd = grid.getGridEl().select('tbody', true).first();
36932             if (tbd) {
36933                 tbd.setSize(width, height - thd.getHeight());
36934             }
36935             */
36936             grid.autoSize();
36937         }
36938     },
36939      
36940     
36941     
36942     beforeSlide : function(){
36943         this.grid.getView().scroller.clip();
36944     },
36945     
36946     afterSlide : function(){
36947         this.grid.getView().scroller.unclip();
36948     },
36949     
36950     destroy : function(){
36951         this.grid.destroy();
36952         delete this.grid;
36953         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36954     }
36955 });
36956
36957 /**
36958  * @class Roo.bootstrap.panel.Nest
36959  * @extends Roo.bootstrap.panel.Content
36960  * @constructor
36961  * Create a new Panel, that can contain a layout.Border.
36962  * 
36963  * 
36964  * @param {Roo.BorderLayout} layout The layout for this panel
36965  * @param {String/Object} config A string to set only the title or a config object
36966  */
36967 Roo.bootstrap.panel.Nest = function(config)
36968 {
36969     // construct with only one argument..
36970     /* FIXME - implement nicer consturctors
36971     if (layout.layout) {
36972         config = layout;
36973         layout = config.layout;
36974         delete config.layout;
36975     }
36976     if (layout.xtype && !layout.getEl) {
36977         // then layout needs constructing..
36978         layout = Roo.factory(layout, Roo);
36979     }
36980     */
36981     
36982     config.el =  config.layout.getEl();
36983     
36984     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36985     
36986     config.layout.monitorWindowResize = false; // turn off autosizing
36987     this.layout = config.layout;
36988     this.layout.getEl().addClass("roo-layout-nested-layout");
36989     
36990     
36991     
36992     
36993 };
36994
36995 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36996
36997     setSize : function(width, height){
36998         if(!this.ignoreResize(width, height)){
36999             var size = this.adjustForComponents(width, height);
37000             var el = this.layout.getEl();
37001             if (size.height < 1) {
37002                 el.setWidth(size.width);   
37003             } else {
37004                 el.setSize(size.width, size.height);
37005             }
37006             var touch = el.dom.offsetWidth;
37007             this.layout.layout();
37008             // ie requires a double layout on the first pass
37009             if(Roo.isIE && !this.initialized){
37010                 this.initialized = true;
37011                 this.layout.layout();
37012             }
37013         }
37014     },
37015     
37016     // activate all subpanels if not currently active..
37017     
37018     setActiveState : function(active){
37019         this.active = active;
37020         this.setActiveClass(active);
37021         
37022         if(!active){
37023             this.fireEvent("deactivate", this);
37024             return;
37025         }
37026         
37027         this.fireEvent("activate", this);
37028         // not sure if this should happen before or after..
37029         if (!this.layout) {
37030             return; // should not happen..
37031         }
37032         var reg = false;
37033         for (var r in this.layout.regions) {
37034             reg = this.layout.getRegion(r);
37035             if (reg.getActivePanel()) {
37036                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37037                 reg.setActivePanel(reg.getActivePanel());
37038                 continue;
37039             }
37040             if (!reg.panels.length) {
37041                 continue;
37042             }
37043             reg.showPanel(reg.getPanel(0));
37044         }
37045         
37046         
37047         
37048         
37049     },
37050     
37051     /**
37052      * Returns the nested BorderLayout for this panel
37053      * @return {Roo.BorderLayout} 
37054      */
37055     getLayout : function(){
37056         return this.layout;
37057     },
37058     
37059      /**
37060      * Adds a xtype elements to the layout of the nested panel
37061      * <pre><code>
37062
37063 panel.addxtype({
37064        xtype : 'ContentPanel',
37065        region: 'west',
37066        items: [ .... ]
37067    }
37068 );
37069
37070 panel.addxtype({
37071         xtype : 'NestedLayoutPanel',
37072         region: 'west',
37073         layout: {
37074            center: { },
37075            west: { }   
37076         },
37077         items : [ ... list of content panels or nested layout panels.. ]
37078    }
37079 );
37080 </code></pre>
37081      * @param {Object} cfg Xtype definition of item to add.
37082      */
37083     addxtype : function(cfg) {
37084         return this.layout.addxtype(cfg);
37085     
37086     }
37087 });        /*
37088  * Based on:
37089  * Ext JS Library 1.1.1
37090  * Copyright(c) 2006-2007, Ext JS, LLC.
37091  *
37092  * Originally Released Under LGPL - original licence link has changed is not relivant.
37093  *
37094  * Fork - LGPL
37095  * <script type="text/javascript">
37096  */
37097 /**
37098  * @class Roo.TabPanel
37099  * @extends Roo.util.Observable
37100  * A lightweight tab container.
37101  * <br><br>
37102  * Usage:
37103  * <pre><code>
37104 // basic tabs 1, built from existing content
37105 var tabs = new Roo.TabPanel("tabs1");
37106 tabs.addTab("script", "View Script");
37107 tabs.addTab("markup", "View Markup");
37108 tabs.activate("script");
37109
37110 // more advanced tabs, built from javascript
37111 var jtabs = new Roo.TabPanel("jtabs");
37112 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37113
37114 // set up the UpdateManager
37115 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37116 var updater = tab2.getUpdateManager();
37117 updater.setDefaultUrl("ajax1.htm");
37118 tab2.on('activate', updater.refresh, updater, true);
37119
37120 // Use setUrl for Ajax loading
37121 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37122 tab3.setUrl("ajax2.htm", null, true);
37123
37124 // Disabled tab
37125 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37126 tab4.disable();
37127
37128 jtabs.activate("jtabs-1");
37129  * </code></pre>
37130  * @constructor
37131  * Create a new TabPanel.
37132  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37133  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37134  */
37135 Roo.bootstrap.panel.Tabs = function(config){
37136     /**
37137     * The container element for this TabPanel.
37138     * @type Roo.Element
37139     */
37140     this.el = Roo.get(config.el);
37141     delete config.el;
37142     if(config){
37143         if(typeof config == "boolean"){
37144             this.tabPosition = config ? "bottom" : "top";
37145         }else{
37146             Roo.apply(this, config);
37147         }
37148     }
37149     
37150     if(this.tabPosition == "bottom"){
37151         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37152         this.el.addClass("roo-tabs-bottom");
37153     }
37154     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37155     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37156     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37157     if(Roo.isIE){
37158         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37159     }
37160     if(this.tabPosition != "bottom"){
37161         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37162          * @type Roo.Element
37163          */
37164         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37165         this.el.addClass("roo-tabs-top");
37166     }
37167     this.items = [];
37168
37169     this.bodyEl.setStyle("position", "relative");
37170
37171     this.active = null;
37172     this.activateDelegate = this.activate.createDelegate(this);
37173
37174     this.addEvents({
37175         /**
37176          * @event tabchange
37177          * Fires when the active tab changes
37178          * @param {Roo.TabPanel} this
37179          * @param {Roo.TabPanelItem} activePanel The new active tab
37180          */
37181         "tabchange": true,
37182         /**
37183          * @event beforetabchange
37184          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37185          * @param {Roo.TabPanel} this
37186          * @param {Object} e Set cancel to true on this object to cancel the tab change
37187          * @param {Roo.TabPanelItem} tab The tab being changed to
37188          */
37189         "beforetabchange" : true
37190     });
37191
37192     Roo.EventManager.onWindowResize(this.onResize, this);
37193     this.cpad = this.el.getPadding("lr");
37194     this.hiddenCount = 0;
37195
37196
37197     // toolbar on the tabbar support...
37198     if (this.toolbar) {
37199         alert("no toolbar support yet");
37200         this.toolbar  = false;
37201         /*
37202         var tcfg = this.toolbar;
37203         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37204         this.toolbar = new Roo.Toolbar(tcfg);
37205         if (Roo.isSafari) {
37206             var tbl = tcfg.container.child('table', true);
37207             tbl.setAttribute('width', '100%');
37208         }
37209         */
37210         
37211     }
37212    
37213
37214
37215     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37216 };
37217
37218 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37219     /*
37220      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37221      */
37222     tabPosition : "top",
37223     /*
37224      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37225      */
37226     currentTabWidth : 0,
37227     /*
37228      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37229      */
37230     minTabWidth : 40,
37231     /*
37232      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37233      */
37234     maxTabWidth : 250,
37235     /*
37236      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37237      */
37238     preferredTabWidth : 175,
37239     /*
37240      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37241      */
37242     resizeTabs : false,
37243     /*
37244      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37245      */
37246     monitorResize : true,
37247     /*
37248      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37249      */
37250     toolbar : false,
37251
37252     /**
37253      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37254      * @param {String} id The id of the div to use <b>or create</b>
37255      * @param {String} text The text for the tab
37256      * @param {String} content (optional) Content to put in the TabPanelItem body
37257      * @param {Boolean} closable (optional) True to create a close icon on the tab
37258      * @return {Roo.TabPanelItem} The created TabPanelItem
37259      */
37260     addTab : function(id, text, content, closable, tpl)
37261     {
37262         var item = new Roo.bootstrap.panel.TabItem({
37263             panel: this,
37264             id : id,
37265             text : text,
37266             closable : closable,
37267             tpl : tpl
37268         });
37269         this.addTabItem(item);
37270         if(content){
37271             item.setContent(content);
37272         }
37273         return item;
37274     },
37275
37276     /**
37277      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37278      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37279      * @return {Roo.TabPanelItem}
37280      */
37281     getTab : function(id){
37282         return this.items[id];
37283     },
37284
37285     /**
37286      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37287      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37288      */
37289     hideTab : function(id){
37290         var t = this.items[id];
37291         if(!t.isHidden()){
37292            t.setHidden(true);
37293            this.hiddenCount++;
37294            this.autoSizeTabs();
37295         }
37296     },
37297
37298     /**
37299      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37300      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37301      */
37302     unhideTab : function(id){
37303         var t = this.items[id];
37304         if(t.isHidden()){
37305            t.setHidden(false);
37306            this.hiddenCount--;
37307            this.autoSizeTabs();
37308         }
37309     },
37310
37311     /**
37312      * Adds an existing {@link Roo.TabPanelItem}.
37313      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37314      */
37315     addTabItem : function(item){
37316         this.items[item.id] = item;
37317         this.items.push(item);
37318       //  if(this.resizeTabs){
37319     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37320   //         this.autoSizeTabs();
37321 //        }else{
37322 //            item.autoSize();
37323        // }
37324     },
37325
37326     /**
37327      * Removes a {@link Roo.TabPanelItem}.
37328      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37329      */
37330     removeTab : function(id){
37331         var items = this.items;
37332         var tab = items[id];
37333         if(!tab) { return; }
37334         var index = items.indexOf(tab);
37335         if(this.active == tab && items.length > 1){
37336             var newTab = this.getNextAvailable(index);
37337             if(newTab) {
37338                 newTab.activate();
37339             }
37340         }
37341         this.stripEl.dom.removeChild(tab.pnode.dom);
37342         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37343             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37344         }
37345         items.splice(index, 1);
37346         delete this.items[tab.id];
37347         tab.fireEvent("close", tab);
37348         tab.purgeListeners();
37349         this.autoSizeTabs();
37350     },
37351
37352     getNextAvailable : function(start){
37353         var items = this.items;
37354         var index = start;
37355         // look for a next tab that will slide over to
37356         // replace the one being removed
37357         while(index < items.length){
37358             var item = items[++index];
37359             if(item && !item.isHidden()){
37360                 return item;
37361             }
37362         }
37363         // if one isn't found select the previous tab (on the left)
37364         index = start;
37365         while(index >= 0){
37366             var item = items[--index];
37367             if(item && !item.isHidden()){
37368                 return item;
37369             }
37370         }
37371         return null;
37372     },
37373
37374     /**
37375      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37376      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37377      */
37378     disableTab : function(id){
37379         var tab = this.items[id];
37380         if(tab && this.active != tab){
37381             tab.disable();
37382         }
37383     },
37384
37385     /**
37386      * Enables a {@link Roo.TabPanelItem} that is disabled.
37387      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37388      */
37389     enableTab : function(id){
37390         var tab = this.items[id];
37391         tab.enable();
37392     },
37393
37394     /**
37395      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37396      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37397      * @return {Roo.TabPanelItem} The TabPanelItem.
37398      */
37399     activate : function(id){
37400         var tab = this.items[id];
37401         if(!tab){
37402             return null;
37403         }
37404         if(tab == this.active || tab.disabled){
37405             return tab;
37406         }
37407         var e = {};
37408         this.fireEvent("beforetabchange", this, e, tab);
37409         if(e.cancel !== true && !tab.disabled){
37410             if(this.active){
37411                 this.active.hide();
37412             }
37413             this.active = this.items[id];
37414             this.active.show();
37415             this.fireEvent("tabchange", this, this.active);
37416         }
37417         return tab;
37418     },
37419
37420     /**
37421      * Gets the active {@link Roo.TabPanelItem}.
37422      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37423      */
37424     getActiveTab : function(){
37425         return this.active;
37426     },
37427
37428     /**
37429      * Updates the tab body element to fit the height of the container element
37430      * for overflow scrolling
37431      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37432      */
37433     syncHeight : function(targetHeight){
37434         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37435         var bm = this.bodyEl.getMargins();
37436         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37437         this.bodyEl.setHeight(newHeight);
37438         return newHeight;
37439     },
37440
37441     onResize : function(){
37442         if(this.monitorResize){
37443             this.autoSizeTabs();
37444         }
37445     },
37446
37447     /**
37448      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37449      */
37450     beginUpdate : function(){
37451         this.updating = true;
37452     },
37453
37454     /**
37455      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37456      */
37457     endUpdate : function(){
37458         this.updating = false;
37459         this.autoSizeTabs();
37460     },
37461
37462     /**
37463      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37464      */
37465     autoSizeTabs : function(){
37466         var count = this.items.length;
37467         var vcount = count - this.hiddenCount;
37468         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37469             return;
37470         }
37471         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37472         var availWidth = Math.floor(w / vcount);
37473         var b = this.stripBody;
37474         if(b.getWidth() > w){
37475             var tabs = this.items;
37476             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37477             if(availWidth < this.minTabWidth){
37478                 /*if(!this.sleft){    // incomplete scrolling code
37479                     this.createScrollButtons();
37480                 }
37481                 this.showScroll();
37482                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37483             }
37484         }else{
37485             if(this.currentTabWidth < this.preferredTabWidth){
37486                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37487             }
37488         }
37489     },
37490
37491     /**
37492      * Returns the number of tabs in this TabPanel.
37493      * @return {Number}
37494      */
37495      getCount : function(){
37496          return this.items.length;
37497      },
37498
37499     /**
37500      * Resizes all the tabs to the passed width
37501      * @param {Number} The new width
37502      */
37503     setTabWidth : function(width){
37504         this.currentTabWidth = width;
37505         for(var i = 0, len = this.items.length; i < len; i++) {
37506                 if(!this.items[i].isHidden()) {
37507                 this.items[i].setWidth(width);
37508             }
37509         }
37510     },
37511
37512     /**
37513      * Destroys this TabPanel
37514      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37515      */
37516     destroy : function(removeEl){
37517         Roo.EventManager.removeResizeListener(this.onResize, this);
37518         for(var i = 0, len = this.items.length; i < len; i++){
37519             this.items[i].purgeListeners();
37520         }
37521         if(removeEl === true){
37522             this.el.update("");
37523             this.el.remove();
37524         }
37525     },
37526     
37527     createStrip : function(container)
37528     {
37529         var strip = document.createElement("nav");
37530         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37531         container.appendChild(strip);
37532         return strip;
37533     },
37534     
37535     createStripList : function(strip)
37536     {
37537         // div wrapper for retard IE
37538         // returns the "tr" element.
37539         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37540         //'<div class="x-tabs-strip-wrap">'+
37541           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37542           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37543         return strip.firstChild; //.firstChild.firstChild.firstChild;
37544     },
37545     createBody : function(container)
37546     {
37547         var body = document.createElement("div");
37548         Roo.id(body, "tab-body");
37549         //Roo.fly(body).addClass("x-tabs-body");
37550         Roo.fly(body).addClass("tab-content");
37551         container.appendChild(body);
37552         return body;
37553     },
37554     createItemBody :function(bodyEl, id){
37555         var body = Roo.getDom(id);
37556         if(!body){
37557             body = document.createElement("div");
37558             body.id = id;
37559         }
37560         //Roo.fly(body).addClass("x-tabs-item-body");
37561         Roo.fly(body).addClass("tab-pane");
37562          bodyEl.insertBefore(body, bodyEl.firstChild);
37563         return body;
37564     },
37565     /** @private */
37566     createStripElements :  function(stripEl, text, closable, tpl)
37567     {
37568         var td = document.createElement("li"); // was td..
37569         
37570         
37571         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37572         
37573         
37574         stripEl.appendChild(td);
37575         /*if(closable){
37576             td.className = "x-tabs-closable";
37577             if(!this.closeTpl){
37578                 this.closeTpl = new Roo.Template(
37579                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37580                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37581                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37582                 );
37583             }
37584             var el = this.closeTpl.overwrite(td, {"text": text});
37585             var close = el.getElementsByTagName("div")[0];
37586             var inner = el.getElementsByTagName("em")[0];
37587             return {"el": el, "close": close, "inner": inner};
37588         } else {
37589         */
37590         // not sure what this is..
37591 //            if(!this.tabTpl){
37592                 //this.tabTpl = new Roo.Template(
37593                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37594                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37595                 //);
37596 //                this.tabTpl = new Roo.Template(
37597 //                   '<a href="#">' +
37598 //                   '<span unselectable="on"' +
37599 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37600 //                            ' >{text}</span></a>'
37601 //                );
37602 //                
37603 //            }
37604
37605
37606             var template = tpl || this.tabTpl || false;
37607             
37608             if(!template){
37609                 
37610                 template = new Roo.Template(
37611                    '<a href="#">' +
37612                    '<span unselectable="on"' +
37613                             (this.disableTooltips ? '' : ' title="{text}"') +
37614                             ' >{text}</span></a>'
37615                 );
37616             }
37617             
37618             switch (typeof(template)) {
37619                 case 'object' :
37620                     break;
37621                 case 'string' :
37622                     template = new Roo.Template(template);
37623                     break;
37624                 default :
37625                     break;
37626             }
37627             
37628             var el = template.overwrite(td, {"text": text});
37629             
37630             var inner = el.getElementsByTagName("span")[0];
37631             
37632             return {"el": el, "inner": inner};
37633             
37634     }
37635         
37636     
37637 });
37638
37639 /**
37640  * @class Roo.TabPanelItem
37641  * @extends Roo.util.Observable
37642  * Represents an individual item (tab plus body) in a TabPanel.
37643  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37644  * @param {String} id The id of this TabPanelItem
37645  * @param {String} text The text for the tab of this TabPanelItem
37646  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37647  */
37648 Roo.bootstrap.panel.TabItem = function(config){
37649     /**
37650      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37651      * @type Roo.TabPanel
37652      */
37653     this.tabPanel = config.panel;
37654     /**
37655      * The id for this TabPanelItem
37656      * @type String
37657      */
37658     this.id = config.id;
37659     /** @private */
37660     this.disabled = false;
37661     /** @private */
37662     this.text = config.text;
37663     /** @private */
37664     this.loaded = false;
37665     this.closable = config.closable;
37666
37667     /**
37668      * The body element for this TabPanelItem.
37669      * @type Roo.Element
37670      */
37671     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37672     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37673     this.bodyEl.setStyle("display", "block");
37674     this.bodyEl.setStyle("zoom", "1");
37675     //this.hideAction();
37676
37677     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37678     /** @private */
37679     this.el = Roo.get(els.el);
37680     this.inner = Roo.get(els.inner, true);
37681     this.textEl = Roo.get(this.el.dom.firstChild, true);
37682     this.pnode = Roo.get(els.el.parentNode, true);
37683 //    this.el.on("mousedown", this.onTabMouseDown, this);
37684     this.el.on("click", this.onTabClick, this);
37685     /** @private */
37686     if(config.closable){
37687         var c = Roo.get(els.close, true);
37688         c.dom.title = this.closeText;
37689         c.addClassOnOver("close-over");
37690         c.on("click", this.closeClick, this);
37691      }
37692
37693     this.addEvents({
37694          /**
37695          * @event activate
37696          * Fires when this tab becomes the active tab.
37697          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37698          * @param {Roo.TabPanelItem} this
37699          */
37700         "activate": true,
37701         /**
37702          * @event beforeclose
37703          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37704          * @param {Roo.TabPanelItem} this
37705          * @param {Object} e Set cancel to true on this object to cancel the close.
37706          */
37707         "beforeclose": true,
37708         /**
37709          * @event close
37710          * Fires when this tab is closed.
37711          * @param {Roo.TabPanelItem} this
37712          */
37713          "close": true,
37714         /**
37715          * @event deactivate
37716          * Fires when this tab is no longer the active tab.
37717          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37718          * @param {Roo.TabPanelItem} this
37719          */
37720          "deactivate" : true
37721     });
37722     this.hidden = false;
37723
37724     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37725 };
37726
37727 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37728            {
37729     purgeListeners : function(){
37730        Roo.util.Observable.prototype.purgeListeners.call(this);
37731        this.el.removeAllListeners();
37732     },
37733     /**
37734      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37735      */
37736     show : function(){
37737         this.pnode.addClass("active");
37738         this.showAction();
37739         if(Roo.isOpera){
37740             this.tabPanel.stripWrap.repaint();
37741         }
37742         this.fireEvent("activate", this.tabPanel, this);
37743     },
37744
37745     /**
37746      * Returns true if this tab is the active tab.
37747      * @return {Boolean}
37748      */
37749     isActive : function(){
37750         return this.tabPanel.getActiveTab() == this;
37751     },
37752
37753     /**
37754      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37755      */
37756     hide : function(){
37757         this.pnode.removeClass("active");
37758         this.hideAction();
37759         this.fireEvent("deactivate", this.tabPanel, this);
37760     },
37761
37762     hideAction : function(){
37763         this.bodyEl.hide();
37764         this.bodyEl.setStyle("position", "absolute");
37765         this.bodyEl.setLeft("-20000px");
37766         this.bodyEl.setTop("-20000px");
37767     },
37768
37769     showAction : function(){
37770         this.bodyEl.setStyle("position", "relative");
37771         this.bodyEl.setTop("");
37772         this.bodyEl.setLeft("");
37773         this.bodyEl.show();
37774     },
37775
37776     /**
37777      * Set the tooltip for the tab.
37778      * @param {String} tooltip The tab's tooltip
37779      */
37780     setTooltip : function(text){
37781         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37782             this.textEl.dom.qtip = text;
37783             this.textEl.dom.removeAttribute('title');
37784         }else{
37785             this.textEl.dom.title = text;
37786         }
37787     },
37788
37789     onTabClick : function(e){
37790         e.preventDefault();
37791         this.tabPanel.activate(this.id);
37792     },
37793
37794     onTabMouseDown : function(e){
37795         e.preventDefault();
37796         this.tabPanel.activate(this.id);
37797     },
37798 /*
37799     getWidth : function(){
37800         return this.inner.getWidth();
37801     },
37802
37803     setWidth : function(width){
37804         var iwidth = width - this.pnode.getPadding("lr");
37805         this.inner.setWidth(iwidth);
37806         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37807         this.pnode.setWidth(width);
37808     },
37809 */
37810     /**
37811      * Show or hide the tab
37812      * @param {Boolean} hidden True to hide or false to show.
37813      */
37814     setHidden : function(hidden){
37815         this.hidden = hidden;
37816         this.pnode.setStyle("display", hidden ? "none" : "");
37817     },
37818
37819     /**
37820      * Returns true if this tab is "hidden"
37821      * @return {Boolean}
37822      */
37823     isHidden : function(){
37824         return this.hidden;
37825     },
37826
37827     /**
37828      * Returns the text for this tab
37829      * @return {String}
37830      */
37831     getText : function(){
37832         return this.text;
37833     },
37834     /*
37835     autoSize : function(){
37836         //this.el.beginMeasure();
37837         this.textEl.setWidth(1);
37838         /*
37839          *  #2804 [new] Tabs in Roojs
37840          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37841          */
37842         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37843         //this.el.endMeasure();
37844     //},
37845
37846     /**
37847      * Sets the text for the tab (Note: this also sets the tooltip text)
37848      * @param {String} text The tab's text and tooltip
37849      */
37850     setText : function(text){
37851         this.text = text;
37852         this.textEl.update(text);
37853         this.setTooltip(text);
37854         //if(!this.tabPanel.resizeTabs){
37855         //    this.autoSize();
37856         //}
37857     },
37858     /**
37859      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37860      */
37861     activate : function(){
37862         this.tabPanel.activate(this.id);
37863     },
37864
37865     /**
37866      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37867      */
37868     disable : function(){
37869         if(this.tabPanel.active != this){
37870             this.disabled = true;
37871             this.pnode.addClass("disabled");
37872         }
37873     },
37874
37875     /**
37876      * Enables this TabPanelItem if it was previously disabled.
37877      */
37878     enable : function(){
37879         this.disabled = false;
37880         this.pnode.removeClass("disabled");
37881     },
37882
37883     /**
37884      * Sets the content for this TabPanelItem.
37885      * @param {String} content The content
37886      * @param {Boolean} loadScripts true to look for and load scripts
37887      */
37888     setContent : function(content, loadScripts){
37889         this.bodyEl.update(content, loadScripts);
37890     },
37891
37892     /**
37893      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37894      * @return {Roo.UpdateManager} The UpdateManager
37895      */
37896     getUpdateManager : function(){
37897         return this.bodyEl.getUpdateManager();
37898     },
37899
37900     /**
37901      * Set a URL to be used to load the content for this TabPanelItem.
37902      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37903      * @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)
37904      * @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)
37905      * @return {Roo.UpdateManager} The UpdateManager
37906      */
37907     setUrl : function(url, params, loadOnce){
37908         if(this.refreshDelegate){
37909             this.un('activate', this.refreshDelegate);
37910         }
37911         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37912         this.on("activate", this.refreshDelegate);
37913         return this.bodyEl.getUpdateManager();
37914     },
37915
37916     /** @private */
37917     _handleRefresh : function(url, params, loadOnce){
37918         if(!loadOnce || !this.loaded){
37919             var updater = this.bodyEl.getUpdateManager();
37920             updater.update(url, params, this._setLoaded.createDelegate(this));
37921         }
37922     },
37923
37924     /**
37925      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37926      *   Will fail silently if the setUrl method has not been called.
37927      *   This does not activate the panel, just updates its content.
37928      */
37929     refresh : function(){
37930         if(this.refreshDelegate){
37931            this.loaded = false;
37932            this.refreshDelegate();
37933         }
37934     },
37935
37936     /** @private */
37937     _setLoaded : function(){
37938         this.loaded = true;
37939     },
37940
37941     /** @private */
37942     closeClick : function(e){
37943         var o = {};
37944         e.stopEvent();
37945         this.fireEvent("beforeclose", this, o);
37946         if(o.cancel !== true){
37947             this.tabPanel.removeTab(this.id);
37948         }
37949     },
37950     /**
37951      * The text displayed in the tooltip for the close icon.
37952      * @type String
37953      */
37954     closeText : "Close this tab"
37955 });
37956 /**
37957 *    This script refer to:
37958 *    Title: International Telephone Input
37959 *    Author: Jack O'Connor
37960 *    Code version:  v12.1.12
37961 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37962 **/
37963
37964 Roo.bootstrap.PhoneInputData = function() {
37965     var d = [
37966       [
37967         "Afghanistan (‫افغانستان‬‎)",
37968         "af",
37969         "93"
37970       ],
37971       [
37972         "Albania (Shqipëri)",
37973         "al",
37974         "355"
37975       ],
37976       [
37977         "Algeria (‫الجزائر‬‎)",
37978         "dz",
37979         "213"
37980       ],
37981       [
37982         "American Samoa",
37983         "as",
37984         "1684"
37985       ],
37986       [
37987         "Andorra",
37988         "ad",
37989         "376"
37990       ],
37991       [
37992         "Angola",
37993         "ao",
37994         "244"
37995       ],
37996       [
37997         "Anguilla",
37998         "ai",
37999         "1264"
38000       ],
38001       [
38002         "Antigua and Barbuda",
38003         "ag",
38004         "1268"
38005       ],
38006       [
38007         "Argentina",
38008         "ar",
38009         "54"
38010       ],
38011       [
38012         "Armenia (Հայաստան)",
38013         "am",
38014         "374"
38015       ],
38016       [
38017         "Aruba",
38018         "aw",
38019         "297"
38020       ],
38021       [
38022         "Australia",
38023         "au",
38024         "61",
38025         0
38026       ],
38027       [
38028         "Austria (Österreich)",
38029         "at",
38030         "43"
38031       ],
38032       [
38033         "Azerbaijan (Azərbaycan)",
38034         "az",
38035         "994"
38036       ],
38037       [
38038         "Bahamas",
38039         "bs",
38040         "1242"
38041       ],
38042       [
38043         "Bahrain (‫البحرين‬‎)",
38044         "bh",
38045         "973"
38046       ],
38047       [
38048         "Bangladesh (বাংলাদেশ)",
38049         "bd",
38050         "880"
38051       ],
38052       [
38053         "Barbados",
38054         "bb",
38055         "1246"
38056       ],
38057       [
38058         "Belarus (Беларусь)",
38059         "by",
38060         "375"
38061       ],
38062       [
38063         "Belgium (België)",
38064         "be",
38065         "32"
38066       ],
38067       [
38068         "Belize",
38069         "bz",
38070         "501"
38071       ],
38072       [
38073         "Benin (Bénin)",
38074         "bj",
38075         "229"
38076       ],
38077       [
38078         "Bermuda",
38079         "bm",
38080         "1441"
38081       ],
38082       [
38083         "Bhutan (འབྲུག)",
38084         "bt",
38085         "975"
38086       ],
38087       [
38088         "Bolivia",
38089         "bo",
38090         "591"
38091       ],
38092       [
38093         "Bosnia and Herzegovina (Босна и Херцеговина)",
38094         "ba",
38095         "387"
38096       ],
38097       [
38098         "Botswana",
38099         "bw",
38100         "267"
38101       ],
38102       [
38103         "Brazil (Brasil)",
38104         "br",
38105         "55"
38106       ],
38107       [
38108         "British Indian Ocean Territory",
38109         "io",
38110         "246"
38111       ],
38112       [
38113         "British Virgin Islands",
38114         "vg",
38115         "1284"
38116       ],
38117       [
38118         "Brunei",
38119         "bn",
38120         "673"
38121       ],
38122       [
38123         "Bulgaria (България)",
38124         "bg",
38125         "359"
38126       ],
38127       [
38128         "Burkina Faso",
38129         "bf",
38130         "226"
38131       ],
38132       [
38133         "Burundi (Uburundi)",
38134         "bi",
38135         "257"
38136       ],
38137       [
38138         "Cambodia (កម្ពុជា)",
38139         "kh",
38140         "855"
38141       ],
38142       [
38143         "Cameroon (Cameroun)",
38144         "cm",
38145         "237"
38146       ],
38147       [
38148         "Canada",
38149         "ca",
38150         "1",
38151         1,
38152         ["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"]
38153       ],
38154       [
38155         "Cape Verde (Kabu Verdi)",
38156         "cv",
38157         "238"
38158       ],
38159       [
38160         "Caribbean Netherlands",
38161         "bq",
38162         "599",
38163         1
38164       ],
38165       [
38166         "Cayman Islands",
38167         "ky",
38168         "1345"
38169       ],
38170       [
38171         "Central African Republic (République centrafricaine)",
38172         "cf",
38173         "236"
38174       ],
38175       [
38176         "Chad (Tchad)",
38177         "td",
38178         "235"
38179       ],
38180       [
38181         "Chile",
38182         "cl",
38183         "56"
38184       ],
38185       [
38186         "China (中国)",
38187         "cn",
38188         "86"
38189       ],
38190       [
38191         "Christmas Island",
38192         "cx",
38193         "61",
38194         2
38195       ],
38196       [
38197         "Cocos (Keeling) Islands",
38198         "cc",
38199         "61",
38200         1
38201       ],
38202       [
38203         "Colombia",
38204         "co",
38205         "57"
38206       ],
38207       [
38208         "Comoros (‫جزر القمر‬‎)",
38209         "km",
38210         "269"
38211       ],
38212       [
38213         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38214         "cd",
38215         "243"
38216       ],
38217       [
38218         "Congo (Republic) (Congo-Brazzaville)",
38219         "cg",
38220         "242"
38221       ],
38222       [
38223         "Cook Islands",
38224         "ck",
38225         "682"
38226       ],
38227       [
38228         "Costa Rica",
38229         "cr",
38230         "506"
38231       ],
38232       [
38233         "Côte d’Ivoire",
38234         "ci",
38235         "225"
38236       ],
38237       [
38238         "Croatia (Hrvatska)",
38239         "hr",
38240         "385"
38241       ],
38242       [
38243         "Cuba",
38244         "cu",
38245         "53"
38246       ],
38247       [
38248         "Curaçao",
38249         "cw",
38250         "599",
38251         0
38252       ],
38253       [
38254         "Cyprus (Κύπρος)",
38255         "cy",
38256         "357"
38257       ],
38258       [
38259         "Czech Republic (Česká republika)",
38260         "cz",
38261         "420"
38262       ],
38263       [
38264         "Denmark (Danmark)",
38265         "dk",
38266         "45"
38267       ],
38268       [
38269         "Djibouti",
38270         "dj",
38271         "253"
38272       ],
38273       [
38274         "Dominica",
38275         "dm",
38276         "1767"
38277       ],
38278       [
38279         "Dominican Republic (República Dominicana)",
38280         "do",
38281         "1",
38282         2,
38283         ["809", "829", "849"]
38284       ],
38285       [
38286         "Ecuador",
38287         "ec",
38288         "593"
38289       ],
38290       [
38291         "Egypt (‫مصر‬‎)",
38292         "eg",
38293         "20"
38294       ],
38295       [
38296         "El Salvador",
38297         "sv",
38298         "503"
38299       ],
38300       [
38301         "Equatorial Guinea (Guinea Ecuatorial)",
38302         "gq",
38303         "240"
38304       ],
38305       [
38306         "Eritrea",
38307         "er",
38308         "291"
38309       ],
38310       [
38311         "Estonia (Eesti)",
38312         "ee",
38313         "372"
38314       ],
38315       [
38316         "Ethiopia",
38317         "et",
38318         "251"
38319       ],
38320       [
38321         "Falkland Islands (Islas Malvinas)",
38322         "fk",
38323         "500"
38324       ],
38325       [
38326         "Faroe Islands (Føroyar)",
38327         "fo",
38328         "298"
38329       ],
38330       [
38331         "Fiji",
38332         "fj",
38333         "679"
38334       ],
38335       [
38336         "Finland (Suomi)",
38337         "fi",
38338         "358",
38339         0
38340       ],
38341       [
38342         "France",
38343         "fr",
38344         "33"
38345       ],
38346       [
38347         "French Guiana (Guyane française)",
38348         "gf",
38349         "594"
38350       ],
38351       [
38352         "French Polynesia (Polynésie française)",
38353         "pf",
38354         "689"
38355       ],
38356       [
38357         "Gabon",
38358         "ga",
38359         "241"
38360       ],
38361       [
38362         "Gambia",
38363         "gm",
38364         "220"
38365       ],
38366       [
38367         "Georgia (საქართველო)",
38368         "ge",
38369         "995"
38370       ],
38371       [
38372         "Germany (Deutschland)",
38373         "de",
38374         "49"
38375       ],
38376       [
38377         "Ghana (Gaana)",
38378         "gh",
38379         "233"
38380       ],
38381       [
38382         "Gibraltar",
38383         "gi",
38384         "350"
38385       ],
38386       [
38387         "Greece (Ελλάδα)",
38388         "gr",
38389         "30"
38390       ],
38391       [
38392         "Greenland (Kalaallit Nunaat)",
38393         "gl",
38394         "299"
38395       ],
38396       [
38397         "Grenada",
38398         "gd",
38399         "1473"
38400       ],
38401       [
38402         "Guadeloupe",
38403         "gp",
38404         "590",
38405         0
38406       ],
38407       [
38408         "Guam",
38409         "gu",
38410         "1671"
38411       ],
38412       [
38413         "Guatemala",
38414         "gt",
38415         "502"
38416       ],
38417       [
38418         "Guernsey",
38419         "gg",
38420         "44",
38421         1
38422       ],
38423       [
38424         "Guinea (Guinée)",
38425         "gn",
38426         "224"
38427       ],
38428       [
38429         "Guinea-Bissau (Guiné Bissau)",
38430         "gw",
38431         "245"
38432       ],
38433       [
38434         "Guyana",
38435         "gy",
38436         "592"
38437       ],
38438       [
38439         "Haiti",
38440         "ht",
38441         "509"
38442       ],
38443       [
38444         "Honduras",
38445         "hn",
38446         "504"
38447       ],
38448       [
38449         "Hong Kong (香港)",
38450         "hk",
38451         "852"
38452       ],
38453       [
38454         "Hungary (Magyarország)",
38455         "hu",
38456         "36"
38457       ],
38458       [
38459         "Iceland (Ísland)",
38460         "is",
38461         "354"
38462       ],
38463       [
38464         "India (भारत)",
38465         "in",
38466         "91"
38467       ],
38468       [
38469         "Indonesia",
38470         "id",
38471         "62"
38472       ],
38473       [
38474         "Iran (‫ایران‬‎)",
38475         "ir",
38476         "98"
38477       ],
38478       [
38479         "Iraq (‫العراق‬‎)",
38480         "iq",
38481         "964"
38482       ],
38483       [
38484         "Ireland",
38485         "ie",
38486         "353"
38487       ],
38488       [
38489         "Isle of Man",
38490         "im",
38491         "44",
38492         2
38493       ],
38494       [
38495         "Israel (‫ישראל‬‎)",
38496         "il",
38497         "972"
38498       ],
38499       [
38500         "Italy (Italia)",
38501         "it",
38502         "39",
38503         0
38504       ],
38505       [
38506         "Jamaica",
38507         "jm",
38508         "1876"
38509       ],
38510       [
38511         "Japan (日本)",
38512         "jp",
38513         "81"
38514       ],
38515       [
38516         "Jersey",
38517         "je",
38518         "44",
38519         3
38520       ],
38521       [
38522         "Jordan (‫الأردن‬‎)",
38523         "jo",
38524         "962"
38525       ],
38526       [
38527         "Kazakhstan (Казахстан)",
38528         "kz",
38529         "7",
38530         1
38531       ],
38532       [
38533         "Kenya",
38534         "ke",
38535         "254"
38536       ],
38537       [
38538         "Kiribati",
38539         "ki",
38540         "686"
38541       ],
38542       [
38543         "Kosovo",
38544         "xk",
38545         "383"
38546       ],
38547       [
38548         "Kuwait (‫الكويت‬‎)",
38549         "kw",
38550         "965"
38551       ],
38552       [
38553         "Kyrgyzstan (Кыргызстан)",
38554         "kg",
38555         "996"
38556       ],
38557       [
38558         "Laos (ລາວ)",
38559         "la",
38560         "856"
38561       ],
38562       [
38563         "Latvia (Latvija)",
38564         "lv",
38565         "371"
38566       ],
38567       [
38568         "Lebanon (‫لبنان‬‎)",
38569         "lb",
38570         "961"
38571       ],
38572       [
38573         "Lesotho",
38574         "ls",
38575         "266"
38576       ],
38577       [
38578         "Liberia",
38579         "lr",
38580         "231"
38581       ],
38582       [
38583         "Libya (‫ليبيا‬‎)",
38584         "ly",
38585         "218"
38586       ],
38587       [
38588         "Liechtenstein",
38589         "li",
38590         "423"
38591       ],
38592       [
38593         "Lithuania (Lietuva)",
38594         "lt",
38595         "370"
38596       ],
38597       [
38598         "Luxembourg",
38599         "lu",
38600         "352"
38601       ],
38602       [
38603         "Macau (澳門)",
38604         "mo",
38605         "853"
38606       ],
38607       [
38608         "Macedonia (FYROM) (Македонија)",
38609         "mk",
38610         "389"
38611       ],
38612       [
38613         "Madagascar (Madagasikara)",
38614         "mg",
38615         "261"
38616       ],
38617       [
38618         "Malawi",
38619         "mw",
38620         "265"
38621       ],
38622       [
38623         "Malaysia",
38624         "my",
38625         "60"
38626       ],
38627       [
38628         "Maldives",
38629         "mv",
38630         "960"
38631       ],
38632       [
38633         "Mali",
38634         "ml",
38635         "223"
38636       ],
38637       [
38638         "Malta",
38639         "mt",
38640         "356"
38641       ],
38642       [
38643         "Marshall Islands",
38644         "mh",
38645         "692"
38646       ],
38647       [
38648         "Martinique",
38649         "mq",
38650         "596"
38651       ],
38652       [
38653         "Mauritania (‫موريتانيا‬‎)",
38654         "mr",
38655         "222"
38656       ],
38657       [
38658         "Mauritius (Moris)",
38659         "mu",
38660         "230"
38661       ],
38662       [
38663         "Mayotte",
38664         "yt",
38665         "262",
38666         1
38667       ],
38668       [
38669         "Mexico (México)",
38670         "mx",
38671         "52"
38672       ],
38673       [
38674         "Micronesia",
38675         "fm",
38676         "691"
38677       ],
38678       [
38679         "Moldova (Republica Moldova)",
38680         "md",
38681         "373"
38682       ],
38683       [
38684         "Monaco",
38685         "mc",
38686         "377"
38687       ],
38688       [
38689         "Mongolia (Монгол)",
38690         "mn",
38691         "976"
38692       ],
38693       [
38694         "Montenegro (Crna Gora)",
38695         "me",
38696         "382"
38697       ],
38698       [
38699         "Montserrat",
38700         "ms",
38701         "1664"
38702       ],
38703       [
38704         "Morocco (‫المغرب‬‎)",
38705         "ma",
38706         "212",
38707         0
38708       ],
38709       [
38710         "Mozambique (Moçambique)",
38711         "mz",
38712         "258"
38713       ],
38714       [
38715         "Myanmar (Burma) (မြန်မာ)",
38716         "mm",
38717         "95"
38718       ],
38719       [
38720         "Namibia (Namibië)",
38721         "na",
38722         "264"
38723       ],
38724       [
38725         "Nauru",
38726         "nr",
38727         "674"
38728       ],
38729       [
38730         "Nepal (नेपाल)",
38731         "np",
38732         "977"
38733       ],
38734       [
38735         "Netherlands (Nederland)",
38736         "nl",
38737         "31"
38738       ],
38739       [
38740         "New Caledonia (Nouvelle-Calédonie)",
38741         "nc",
38742         "687"
38743       ],
38744       [
38745         "New Zealand",
38746         "nz",
38747         "64"
38748       ],
38749       [
38750         "Nicaragua",
38751         "ni",
38752         "505"
38753       ],
38754       [
38755         "Niger (Nijar)",
38756         "ne",
38757         "227"
38758       ],
38759       [
38760         "Nigeria",
38761         "ng",
38762         "234"
38763       ],
38764       [
38765         "Niue",
38766         "nu",
38767         "683"
38768       ],
38769       [
38770         "Norfolk Island",
38771         "nf",
38772         "672"
38773       ],
38774       [
38775         "North Korea (조선 민주주의 인민 공화국)",
38776         "kp",
38777         "850"
38778       ],
38779       [
38780         "Northern Mariana Islands",
38781         "mp",
38782         "1670"
38783       ],
38784       [
38785         "Norway (Norge)",
38786         "no",
38787         "47",
38788         0
38789       ],
38790       [
38791         "Oman (‫عُمان‬‎)",
38792         "om",
38793         "968"
38794       ],
38795       [
38796         "Pakistan (‫پاکستان‬‎)",
38797         "pk",
38798         "92"
38799       ],
38800       [
38801         "Palau",
38802         "pw",
38803         "680"
38804       ],
38805       [
38806         "Palestine (‫فلسطين‬‎)",
38807         "ps",
38808         "970"
38809       ],
38810       [
38811         "Panama (Panamá)",
38812         "pa",
38813         "507"
38814       ],
38815       [
38816         "Papua New Guinea",
38817         "pg",
38818         "675"
38819       ],
38820       [
38821         "Paraguay",
38822         "py",
38823         "595"
38824       ],
38825       [
38826         "Peru (Perú)",
38827         "pe",
38828         "51"
38829       ],
38830       [
38831         "Philippines",
38832         "ph",
38833         "63"
38834       ],
38835       [
38836         "Poland (Polska)",
38837         "pl",
38838         "48"
38839       ],
38840       [
38841         "Portugal",
38842         "pt",
38843         "351"
38844       ],
38845       [
38846         "Puerto Rico",
38847         "pr",
38848         "1",
38849         3,
38850         ["787", "939"]
38851       ],
38852       [
38853         "Qatar (‫قطر‬‎)",
38854         "qa",
38855         "974"
38856       ],
38857       [
38858         "Réunion (La Réunion)",
38859         "re",
38860         "262",
38861         0
38862       ],
38863       [
38864         "Romania (România)",
38865         "ro",
38866         "40"
38867       ],
38868       [
38869         "Russia (Россия)",
38870         "ru",
38871         "7",
38872         0
38873       ],
38874       [
38875         "Rwanda",
38876         "rw",
38877         "250"
38878       ],
38879       [
38880         "Saint Barthélemy",
38881         "bl",
38882         "590",
38883         1
38884       ],
38885       [
38886         "Saint Helena",
38887         "sh",
38888         "290"
38889       ],
38890       [
38891         "Saint Kitts and Nevis",
38892         "kn",
38893         "1869"
38894       ],
38895       [
38896         "Saint Lucia",
38897         "lc",
38898         "1758"
38899       ],
38900       [
38901         "Saint Martin (Saint-Martin (partie française))",
38902         "mf",
38903         "590",
38904         2
38905       ],
38906       [
38907         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38908         "pm",
38909         "508"
38910       ],
38911       [
38912         "Saint Vincent and the Grenadines",
38913         "vc",
38914         "1784"
38915       ],
38916       [
38917         "Samoa",
38918         "ws",
38919         "685"
38920       ],
38921       [
38922         "San Marino",
38923         "sm",
38924         "378"
38925       ],
38926       [
38927         "São Tomé and Príncipe (São Tomé e Príncipe)",
38928         "st",
38929         "239"
38930       ],
38931       [
38932         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38933         "sa",
38934         "966"
38935       ],
38936       [
38937         "Senegal (Sénégal)",
38938         "sn",
38939         "221"
38940       ],
38941       [
38942         "Serbia (Србија)",
38943         "rs",
38944         "381"
38945       ],
38946       [
38947         "Seychelles",
38948         "sc",
38949         "248"
38950       ],
38951       [
38952         "Sierra Leone",
38953         "sl",
38954         "232"
38955       ],
38956       [
38957         "Singapore",
38958         "sg",
38959         "65"
38960       ],
38961       [
38962         "Sint Maarten",
38963         "sx",
38964         "1721"
38965       ],
38966       [
38967         "Slovakia (Slovensko)",
38968         "sk",
38969         "421"
38970       ],
38971       [
38972         "Slovenia (Slovenija)",
38973         "si",
38974         "386"
38975       ],
38976       [
38977         "Solomon Islands",
38978         "sb",
38979         "677"
38980       ],
38981       [
38982         "Somalia (Soomaaliya)",
38983         "so",
38984         "252"
38985       ],
38986       [
38987         "South Africa",
38988         "za",
38989         "27"
38990       ],
38991       [
38992         "South Korea (대한민국)",
38993         "kr",
38994         "82"
38995       ],
38996       [
38997         "South Sudan (‫جنوب السودان‬‎)",
38998         "ss",
38999         "211"
39000       ],
39001       [
39002         "Spain (España)",
39003         "es",
39004         "34"
39005       ],
39006       [
39007         "Sri Lanka (ශ්‍රී ලංකාව)",
39008         "lk",
39009         "94"
39010       ],
39011       [
39012         "Sudan (‫السودان‬‎)",
39013         "sd",
39014         "249"
39015       ],
39016       [
39017         "Suriname",
39018         "sr",
39019         "597"
39020       ],
39021       [
39022         "Svalbard and Jan Mayen",
39023         "sj",
39024         "47",
39025         1
39026       ],
39027       [
39028         "Swaziland",
39029         "sz",
39030         "268"
39031       ],
39032       [
39033         "Sweden (Sverige)",
39034         "se",
39035         "46"
39036       ],
39037       [
39038         "Switzerland (Schweiz)",
39039         "ch",
39040         "41"
39041       ],
39042       [
39043         "Syria (‫سوريا‬‎)",
39044         "sy",
39045         "963"
39046       ],
39047       [
39048         "Taiwan (台灣)",
39049         "tw",
39050         "886"
39051       ],
39052       [
39053         "Tajikistan",
39054         "tj",
39055         "992"
39056       ],
39057       [
39058         "Tanzania",
39059         "tz",
39060         "255"
39061       ],
39062       [
39063         "Thailand (ไทย)",
39064         "th",
39065         "66"
39066       ],
39067       [
39068         "Timor-Leste",
39069         "tl",
39070         "670"
39071       ],
39072       [
39073         "Togo",
39074         "tg",
39075         "228"
39076       ],
39077       [
39078         "Tokelau",
39079         "tk",
39080         "690"
39081       ],
39082       [
39083         "Tonga",
39084         "to",
39085         "676"
39086       ],
39087       [
39088         "Trinidad and Tobago",
39089         "tt",
39090         "1868"
39091       ],
39092       [
39093         "Tunisia (‫تونس‬‎)",
39094         "tn",
39095         "216"
39096       ],
39097       [
39098         "Turkey (Türkiye)",
39099         "tr",
39100         "90"
39101       ],
39102       [
39103         "Turkmenistan",
39104         "tm",
39105         "993"
39106       ],
39107       [
39108         "Turks and Caicos Islands",
39109         "tc",
39110         "1649"
39111       ],
39112       [
39113         "Tuvalu",
39114         "tv",
39115         "688"
39116       ],
39117       [
39118         "U.S. Virgin Islands",
39119         "vi",
39120         "1340"
39121       ],
39122       [
39123         "Uganda",
39124         "ug",
39125         "256"
39126       ],
39127       [
39128         "Ukraine (Україна)",
39129         "ua",
39130         "380"
39131       ],
39132       [
39133         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39134         "ae",
39135         "971"
39136       ],
39137       [
39138         "United Kingdom",
39139         "gb",
39140         "44",
39141         0
39142       ],
39143       [
39144         "United States",
39145         "us",
39146         "1",
39147         0
39148       ],
39149       [
39150         "Uruguay",
39151         "uy",
39152         "598"
39153       ],
39154       [
39155         "Uzbekistan (Oʻzbekiston)",
39156         "uz",
39157         "998"
39158       ],
39159       [
39160         "Vanuatu",
39161         "vu",
39162         "678"
39163       ],
39164       [
39165         "Vatican City (Città del Vaticano)",
39166         "va",
39167         "39",
39168         1
39169       ],
39170       [
39171         "Venezuela",
39172         "ve",
39173         "58"
39174       ],
39175       [
39176         "Vietnam (Việt Nam)",
39177         "vn",
39178         "84"
39179       ],
39180       [
39181         "Wallis and Futuna (Wallis-et-Futuna)",
39182         "wf",
39183         "681"
39184       ],
39185       [
39186         "Western Sahara (‫الصحراء الغربية‬‎)",
39187         "eh",
39188         "212",
39189         1
39190       ],
39191       [
39192         "Yemen (‫اليمن‬‎)",
39193         "ye",
39194         "967"
39195       ],
39196       [
39197         "Zambia",
39198         "zm",
39199         "260"
39200       ],
39201       [
39202         "Zimbabwe",
39203         "zw",
39204         "263"
39205       ],
39206       [
39207         "Åland Islands",
39208         "ax",
39209         "358",
39210         1
39211       ]
39212   ];
39213   
39214   return d;
39215 }/**
39216 *    This script refer to:
39217 *    Title: International Telephone Input
39218 *    Author: Jack O'Connor
39219 *    Code version:  v12.1.12
39220 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39221 **/
39222
39223 /**
39224  * @class Roo.bootstrap.PhoneInput
39225  * @extends Roo.bootstrap.TriggerField
39226  * An input with International dial-code selection
39227  
39228  * @cfg {String} defaultDialCode default '+852'
39229  * @cfg {Array} preferedCountries default []
39230   
39231  * @constructor
39232  * Create a new PhoneInput.
39233  * @param {Object} config Configuration options
39234  */
39235
39236 Roo.bootstrap.PhoneInput = function(config) {
39237     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39238 };
39239
39240 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39241         
39242         listWidth: undefined,
39243         
39244         selectedClass: 'active',
39245         
39246         invalidClass : "has-warning",
39247         
39248         validClass: 'has-success',
39249         
39250         allowed: '0123456789',
39251         
39252         /**
39253          * @cfg {String} defaultDialCode The default dial code when initializing the input
39254          */
39255         defaultDialCode: '+852',
39256         
39257         /**
39258          * @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
39259          */
39260         preferedCountries: false,
39261         
39262         getAutoCreate : function()
39263         {
39264             var data = Roo.bootstrap.PhoneInputData();
39265             var align = this.labelAlign || this.parentLabelAlign();
39266             var id = Roo.id();
39267             
39268             this.allCountries = [];
39269             this.dialCodeMapping = [];
39270             
39271             for (var i = 0; i < data.length; i++) {
39272               var c = data[i];
39273               this.allCountries[i] = {
39274                 name: c[0],
39275                 iso2: c[1],
39276                 dialCode: c[2],
39277                 priority: c[3] || 0,
39278                 areaCodes: c[4] || null
39279               };
39280               this.dialCodeMapping[c[2]] = {
39281                   name: c[0],
39282                   iso2: c[1],
39283                   priority: c[3] || 0,
39284                   areaCodes: c[4] || null
39285               };
39286             }
39287             
39288             var cfg = {
39289                 cls: 'form-group',
39290                 cn: []
39291             };
39292             
39293             var input =  {
39294                 tag: 'input',
39295                 id : id,
39296                 cls : 'form-control tel-input',
39297                 autocomplete: 'new-password'
39298             };
39299             
39300             var hiddenInput = {
39301                 tag: 'input',
39302                 type: 'hidden',
39303                 cls: 'hidden-tel-input'
39304             };
39305             
39306             if (this.name) {
39307                 hiddenInput.name = this.name;
39308             }
39309             
39310             if (this.disabled) {
39311                 input.disabled = true;
39312             }
39313             
39314             var flag_container = {
39315                 tag: 'div',
39316                 cls: 'flag-box',
39317                 cn: [
39318                     {
39319                         tag: 'div',
39320                         cls: 'flag'
39321                     },
39322                     {
39323                         tag: 'div',
39324                         cls: 'caret'
39325                     }
39326                 ]
39327             };
39328             
39329             var box = {
39330                 tag: 'div',
39331                 cls: this.hasFeedback ? 'has-feedback' : '',
39332                 cn: [
39333                     hiddenInput,
39334                     input,
39335                     {
39336                         tag: 'input',
39337                         cls: 'dial-code-holder',
39338                         disabled: true
39339                     }
39340                 ]
39341             };
39342             
39343             var container = {
39344                 cls: 'roo-select2-container input-group',
39345                 cn: [
39346                     flag_container,
39347                     box
39348                 ]
39349             };
39350             
39351             if (this.fieldLabel.length) {
39352                 var indicator = {
39353                     tag: 'i',
39354                     tooltip: 'This field is required'
39355                 };
39356                 
39357                 var label = {
39358                     tag: 'label',
39359                     'for':  id,
39360                     cls: 'control-label',
39361                     cn: []
39362                 };
39363                 
39364                 var label_text = {
39365                     tag: 'span',
39366                     html: this.fieldLabel
39367                 };
39368                 
39369                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39370                 label.cn = [
39371                     indicator,
39372                     label_text
39373                 ];
39374                 
39375                 if(this.indicatorpos == 'right') {
39376                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39377                     label.cn = [
39378                         label_text,
39379                         indicator
39380                     ];
39381                 }
39382                 
39383                 if(align == 'left') {
39384                     container = {
39385                         tag: 'div',
39386                         cn: [
39387                             container
39388                         ]
39389                     };
39390                     
39391                     if(this.labelWidth > 12){
39392                         label.style = "width: " + this.labelWidth + 'px';
39393                     }
39394                     if(this.labelWidth < 13 && this.labelmd == 0){
39395                         this.labelmd = this.labelWidth;
39396                     }
39397                     if(this.labellg > 0){
39398                         label.cls += ' col-lg-' + this.labellg;
39399                         input.cls += ' col-lg-' + (12 - this.labellg);
39400                     }
39401                     if(this.labelmd > 0){
39402                         label.cls += ' col-md-' + this.labelmd;
39403                         container.cls += ' col-md-' + (12 - this.labelmd);
39404                     }
39405                     if(this.labelsm > 0){
39406                         label.cls += ' col-sm-' + this.labelsm;
39407                         container.cls += ' col-sm-' + (12 - this.labelsm);
39408                     }
39409                     if(this.labelxs > 0){
39410                         label.cls += ' col-xs-' + this.labelxs;
39411                         container.cls += ' col-xs-' + (12 - this.labelxs);
39412                     }
39413                 }
39414             }
39415             
39416             cfg.cn = [
39417                 label,
39418                 container
39419             ];
39420             
39421             var settings = this;
39422             
39423             ['xs','sm','md','lg'].map(function(size){
39424                 if (settings[size]) {
39425                     cfg.cls += ' col-' + size + '-' + settings[size];
39426                 }
39427             });
39428             
39429             this.store = new Roo.data.Store({
39430                 proxy : new Roo.data.MemoryProxy({}),
39431                 reader : new Roo.data.JsonReader({
39432                     fields : [
39433                         {
39434                             'name' : 'name',
39435                             'type' : 'string'
39436                         },
39437                         {
39438                             'name' : 'iso2',
39439                             'type' : 'string'
39440                         },
39441                         {
39442                             'name' : 'dialCode',
39443                             'type' : 'string'
39444                         },
39445                         {
39446                             'name' : 'priority',
39447                             'type' : 'string'
39448                         },
39449                         {
39450                             'name' : 'areaCodes',
39451                             'type' : 'string'
39452                         }
39453                     ]
39454                 })
39455             });
39456             
39457             if(!this.preferedCountries) {
39458                 this.preferedCountries = [
39459                     'hk',
39460                     'gb',
39461                     'us'
39462                 ];
39463             }
39464             
39465             var p = this.preferedCountries.reverse();
39466             
39467             if(p) {
39468                 for (var i = 0; i < p.length; i++) {
39469                     for (var j = 0; j < this.allCountries.length; j++) {
39470                         if(this.allCountries[j].iso2 == p[i]) {
39471                             var t = this.allCountries[j];
39472                             this.allCountries.splice(j,1);
39473                             this.allCountries.unshift(t);
39474                         }
39475                     } 
39476                 }
39477             }
39478             
39479             this.store.proxy.data = {
39480                 success: true,
39481                 data: this.allCountries
39482             };
39483             
39484             return cfg;
39485         },
39486         
39487         initEvents : function()
39488         {
39489             this.createList();
39490             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39491             
39492             this.indicator = this.indicatorEl();
39493             this.flag = this.flagEl();
39494             this.dialCodeHolder = this.dialCodeHolderEl();
39495             
39496             this.trigger = this.el.select('div.flag-box',true).first();
39497             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39498             
39499             var _this = this;
39500             
39501             (function(){
39502                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39503                 _this.list.setWidth(lw);
39504             }).defer(100);
39505             
39506             this.list.on('mouseover', this.onViewOver, this);
39507             this.list.on('mousemove', this.onViewMove, this);
39508             this.inputEl().on("keyup", this.onKeyUp, this);
39509             
39510             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39511
39512             this.view = new Roo.View(this.list, this.tpl, {
39513                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39514             });
39515             
39516             this.view.on('click', this.onViewClick, this);
39517             this.setValue(this.defaultDialCode);
39518         },
39519         
39520         onTriggerClick : function(e)
39521         {
39522             Roo.log('trigger click');
39523             if(this.disabled){
39524                 return;
39525             }
39526             
39527             if(this.isExpanded()){
39528                 this.collapse();
39529                 this.hasFocus = false;
39530             }else {
39531                 this.store.load({});
39532                 this.hasFocus = true;
39533                 this.expand();
39534             }
39535         },
39536         
39537         isExpanded : function()
39538         {
39539             return this.list.isVisible();
39540         },
39541         
39542         collapse : function()
39543         {
39544             if(!this.isExpanded()){
39545                 return;
39546             }
39547             this.list.hide();
39548             Roo.get(document).un('mousedown', this.collapseIf, this);
39549             Roo.get(document).un('mousewheel', this.collapseIf, this);
39550             this.fireEvent('collapse', this);
39551             this.validate();
39552         },
39553         
39554         expand : function()
39555         {
39556             Roo.log('expand');
39557
39558             if(this.isExpanded() || !this.hasFocus){
39559                 return;
39560             }
39561             
39562             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39563             this.list.setWidth(lw);
39564             
39565             this.list.show();
39566             this.restrictHeight();
39567             
39568             Roo.get(document).on('mousedown', this.collapseIf, this);
39569             Roo.get(document).on('mousewheel', this.collapseIf, this);
39570             
39571             this.fireEvent('expand', this);
39572         },
39573         
39574         restrictHeight : function()
39575         {
39576             this.list.alignTo(this.inputEl(), this.listAlign);
39577             this.list.alignTo(this.inputEl(), this.listAlign);
39578         },
39579         
39580         onViewOver : function(e, t)
39581         {
39582             if(this.inKeyMode){
39583                 return;
39584             }
39585             var item = this.view.findItemFromChild(t);
39586             
39587             if(item){
39588                 var index = this.view.indexOf(item);
39589                 this.select(index, false);
39590             }
39591         },
39592
39593         // private
39594         onViewClick : function(view, doFocus, el, e)
39595         {
39596             var index = this.view.getSelectedIndexes()[0];
39597             
39598             var r = this.store.getAt(index);
39599             
39600             if(r){
39601                 this.onSelect(r, index);
39602             }
39603             if(doFocus !== false && !this.blockFocus){
39604                 this.inputEl().focus();
39605             }
39606         },
39607         
39608         onViewMove : function(e, t)
39609         {
39610             this.inKeyMode = false;
39611         },
39612         
39613         select : function(index, scrollIntoView)
39614         {
39615             this.selectedIndex = index;
39616             this.view.select(index);
39617             if(scrollIntoView !== false){
39618                 var el = this.view.getNode(index);
39619                 if(el){
39620                     this.list.scrollChildIntoView(el, false);
39621                 }
39622             }
39623         },
39624         
39625         createList : function()
39626         {
39627             this.list = Roo.get(document.body).createChild({
39628                 tag: 'ul',
39629                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39630                 style: 'display:none'
39631             });
39632             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39633         },
39634         
39635         collapseIf : function(e)
39636         {
39637             var in_combo  = e.within(this.el);
39638             var in_list =  e.within(this.list);
39639             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39640             
39641             if (in_combo || in_list || is_list) {
39642                 return;
39643             }
39644             this.collapse();
39645         },
39646         
39647         onSelect : function(record, index)
39648         {
39649             if(this.fireEvent('beforeselect', this, record, index) !== false){
39650                 
39651                 this.setFlagClass(record.data.iso2);
39652                 this.setDialCode(record.data.dialCode);
39653                 this.hasFocus = false;
39654                 this.collapse();
39655                 this.fireEvent('select', this, record, index);
39656             }
39657         },
39658         
39659         flagEl : function()
39660         {
39661             var flag = this.el.select('div.flag',true).first();
39662             if(!flag){
39663                 return false;
39664             }
39665             return flag;
39666         },
39667         
39668         dialCodeHolderEl : function()
39669         {
39670             var d = this.el.select('input.dial-code-holder',true).first();
39671             if(!d){
39672                 return false;
39673             }
39674             return d;
39675         },
39676         
39677         setDialCode : function(v)
39678         {
39679             this.dialCodeHolder.dom.value = '+'+v;
39680         },
39681         
39682         setFlagClass : function(n)
39683         {
39684             this.flag.dom.className = 'flag '+n;
39685         },
39686         
39687         getValue : function()
39688         {
39689             var v = this.inputEl().getValue();
39690             if(this.dialCodeHolder) {
39691                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39692             }
39693             return v;
39694         },
39695         
39696         setValue : function(v)
39697         {
39698             var d = this.getDialCode(v);
39699             
39700             //invalid dial code
39701             if(v.length == 0 || !d || d.length == 0) {
39702                 if(this.rendered){
39703                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39704                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39705                 }
39706                 return;
39707             }
39708             
39709             //valid dial code
39710             this.setFlagClass(this.dialCodeMapping[d].iso2);
39711             this.setDialCode(d);
39712             this.inputEl().dom.value = v.replace('+'+d,'');
39713             this.hiddenEl().dom.value = this.getValue();
39714             
39715             this.validate();
39716         },
39717         
39718         getDialCode : function(v = '')
39719         {
39720             if (v.length == 0) {
39721                 return this.dialCodeHolder.dom.value;
39722             }
39723             
39724             var dialCode = "";
39725             if (v.charAt(0) != "+") {
39726                 return false;
39727             }
39728             var numericChars = "";
39729             for (var i = 1; i < v.length; i++) {
39730               var c = v.charAt(i);
39731               if (!isNaN(c)) {
39732                 numericChars += c;
39733                 if (this.dialCodeMapping[numericChars]) {
39734                   dialCode = v.substr(1, i);
39735                 }
39736                 if (numericChars.length == 4) {
39737                   break;
39738                 }
39739               }
39740             }
39741             return dialCode;
39742         },
39743         
39744         reset : function()
39745         {
39746             this.setValue(this.defaultDialCode);
39747             this.validate();
39748         },
39749         
39750         hiddenEl : function()
39751         {
39752             return this.el.select('input.hidden-tel-input',true).first();
39753         },
39754         
39755         onKeyUp : function(e){
39756             
39757             var k = e.getKey();
39758             var c = e.getCharCode();
39759             
39760             if(
39761                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39762                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39763             ){
39764                 e.stopEvent();
39765             }
39766             
39767             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39768             //     return;
39769             // }
39770             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39771                 e.stopEvent();
39772             }
39773             
39774             this.setValue(this.getValue());
39775         }
39776         
39777 });
39778 /**
39779  * @class Roo.bootstrap.MoneyField
39780  * @extends Roo.bootstrap.ComboBox
39781  * Bootstrap MoneyField class
39782  * 
39783  * @constructor
39784  * Create a new MoneyField.
39785  * @param {Object} config Configuration options
39786  */
39787
39788 Roo.bootstrap.MoneyField = function(config) {
39789     
39790     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39791     
39792 };
39793
39794 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39795     
39796     /**
39797      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39798      */
39799     allowDecimals : true,
39800     /**
39801      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39802      */
39803     decimalSeparator : ".",
39804     /**
39805      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39806      */
39807     decimalPrecision : 2,
39808     /**
39809      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39810      */
39811     allowNegative : true,
39812     /**
39813      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39814      */
39815     minValue : Number.NEGATIVE_INFINITY,
39816     /**
39817      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39818      */
39819     maxValue : Number.MAX_VALUE,
39820     /**
39821      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39822      */
39823     minText : "The minimum value for this field is {0}",
39824     /**
39825      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39826      */
39827     maxText : "The maximum value for this field is {0}",
39828     /**
39829      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39830      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39831      */
39832     nanText : "{0} is not a valid number",
39833     /**
39834      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39835      */
39836     castInt : true,
39837     
39838     inputlg : 9,
39839     inputmd : 9,
39840     inputsm : 9,
39841     inputxs : 6,
39842     
39843     store : false,
39844     
39845     getAutoCreate : function()
39846     {
39847         var align = this.labelAlign || this.parentLabelAlign();
39848         
39849         var id = Roo.id();
39850
39851         var cfg = {
39852             cls: 'form-group',
39853             cn: []
39854         };
39855
39856         var input =  {
39857             tag: 'input',
39858             id : id,
39859             cls : 'form-control roo-money-amount-input',
39860             autocomplete: 'new-password'
39861         };
39862         
39863         if (this.name) {
39864             input.name = this.name;
39865         }
39866
39867         if (this.disabled) {
39868             input.disabled = true;
39869         }
39870
39871         var clg = 12 - this.inputlg;
39872         var cmd = 12 - this.inputmd;
39873         var csm = 12 - this.inputsm;
39874         var cxs = 12 - this.inputxs;
39875         
39876         var container = {
39877             tag : 'div',
39878             cls : 'row roo-money-field',
39879             cn : [
39880                 {
39881                     tag : 'div',
39882                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39883                     cn : [
39884                         {
39885                             tag : 'div',
39886                             cls: 'roo-select2-container input-group',
39887                             cn: [
39888                                 {
39889                                     tag : 'input',
39890                                     cls : 'form-control roo-money-currency-input',
39891                                     autocomplete: 'new-password'
39892                                 },
39893                                 {
39894                                     tag :'span',
39895                                     cls : 'input-group-addon',
39896                                     cn : [
39897                                         {
39898                                             tag: 'span',
39899                                             cls: 'caret'
39900                                         }
39901                                     ]
39902                                 }
39903                             ]
39904                         }
39905                     ]
39906                 },
39907                 {
39908                     tag : 'div',
39909                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39910                     cn : [
39911                         {
39912                             tag: 'div',
39913                             cls: this.hasFeedback ? 'has-feedback' : '',
39914                             cn: [
39915                                 input
39916                             ]
39917                         }
39918                     ]
39919                 }
39920             ]
39921             
39922         };
39923         
39924         if (this.fieldLabel.length) {
39925             var indicator = {
39926                 tag: 'i',
39927                 tooltip: 'This field is required'
39928             };
39929
39930             var label = {
39931                 tag: 'label',
39932                 'for':  id,
39933                 cls: 'control-label',
39934                 cn: []
39935             };
39936
39937             var label_text = {
39938                 tag: 'span',
39939                 html: this.fieldLabel
39940             };
39941
39942             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39943             label.cn = [
39944                 indicator,
39945                 label_text
39946             ];
39947
39948             if(this.indicatorpos == 'right') {
39949                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39950                 label.cn = [
39951                     label_text,
39952                     indicator
39953                 ];
39954             }
39955
39956             if(align == 'left') {
39957                 container = {
39958                     tag: 'div',
39959                     cn: [
39960                         container
39961                     ]
39962                 };
39963
39964                 if(this.labelWidth > 12){
39965                     label.style = "width: " + this.labelWidth + 'px';
39966                 }
39967                 if(this.labelWidth < 13 && this.labelmd == 0){
39968                     this.labelmd = this.labelWidth;
39969                 }
39970                 if(this.labellg > 0){
39971                     label.cls += ' col-lg-' + this.labellg;
39972                     input.cls += ' col-lg-' + (12 - this.labellg);
39973                 }
39974                 if(this.labelmd > 0){
39975                     label.cls += ' col-md-' + this.labelmd;
39976                     container.cls += ' col-md-' + (12 - this.labelmd);
39977                 }
39978                 if(this.labelsm > 0){
39979                     label.cls += ' col-sm-' + this.labelsm;
39980                     container.cls += ' col-sm-' + (12 - this.labelsm);
39981                 }
39982                 if(this.labelxs > 0){
39983                     label.cls += ' col-xs-' + this.labelxs;
39984                     container.cls += ' col-xs-' + (12 - this.labelxs);
39985                 }
39986             }
39987         }
39988
39989         cfg.cn = [
39990             label,
39991             container
39992         ];
39993
39994         var settings = this;
39995
39996         ['xs','sm','md','lg'].map(function(size){
39997             if (settings[size]) {
39998                 cfg.cls += ' col-' + size + '-' + settings[size];
39999             }
40000         });
40001         
40002         return cfg;
40003         
40004     },
40005     
40006     initEvents : function()
40007     {
40008         this.indicator = this.indicatorEl();
40009         
40010         this.initCurrencyEvent();
40011         
40012         this.initNumberEvent();
40013         
40014     },
40015     
40016     initCurrencyEvent : function()
40017     {
40018         if (!this.store) {
40019             throw "can not find store for combo";
40020         }
40021         
40022         this.store = Roo.factory(this.store, Roo.data);
40023         this.store.parent = this;
40024         
40025         this.createList();
40026         
40027         this.triggerEl = this.el.select('.input-group-addon', true).first();
40028         
40029         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40030         
40031         var _this = this;
40032         
40033         (function(){
40034             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40035             _this.list.setWidth(lw);
40036         }).defer(100);
40037         
40038         this.list.on('mouseover', this.onViewOver, this);
40039         this.list.on('mousemove', this.onViewMove, this);
40040         this.list.on('scroll', this.onViewScroll, this);
40041         
40042         if(!this.tpl){
40043             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40044         }
40045         
40046         this.view = new Roo.View(this.list, this.tpl, {
40047             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40048         });
40049         
40050         this.view.on('click', this.onViewClick, this);
40051         
40052         this.store.on('beforeload', this.onBeforeLoad, this);
40053         this.store.on('load', this.onLoad, this);
40054         this.store.on('loadexception', this.onLoadException, this);
40055         
40056         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40057             "up" : function(e){
40058                 this.inKeyMode = true;
40059                 this.selectPrev();
40060             },
40061
40062             "down" : function(e){
40063                 if(!this.isExpanded()){
40064                     this.onTriggerClick();
40065                 }else{
40066                     this.inKeyMode = true;
40067                     this.selectNext();
40068                 }
40069             },
40070
40071             "enter" : function(e){
40072                 this.collapse();
40073                 
40074                 if(this.fireEvent("specialkey", this, e)){
40075                     this.onViewClick(false);
40076                 }
40077                 
40078                 return true;
40079             },
40080
40081             "esc" : function(e){
40082                 this.collapse();
40083             },
40084
40085             "tab" : function(e){
40086                 this.collapse();
40087                 
40088                 if(this.fireEvent("specialkey", this, e)){
40089                     this.onViewClick(false);
40090                 }
40091                 
40092                 return true;
40093             },
40094
40095             scope : this,
40096
40097             doRelay : function(foo, bar, hname){
40098                 if(hname == 'down' || this.scope.isExpanded()){
40099                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40100                 }
40101                 return true;
40102             },
40103
40104             forceKeyDown: true
40105         });
40106         
40107         this.queryDelay = Math.max(this.queryDelay || 10,
40108                 this.mode == 'local' ? 10 : 250);
40109         
40110         
40111         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
40112         
40113         if(this.typeAhead){
40114             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
40115         }
40116         
40117         this.currencyEl().on("keyup", this.onCurrencyKeyUp, this);
40118         
40119     },
40120     
40121     initNumberEvent : function(e)
40122     {
40123         this.inputEl().on("keydown" , this.fireKey,  this);
40124         this.inputEl().on("focus", this.onFocus,  this);
40125         this.inputEl().on("blur", this.onBlur,  this);
40126         
40127         this.inputEl().relayEvent('keyup', this);
40128         
40129         if(this.indicator){
40130             this.indicator.addClass('invisible');
40131         }
40132  
40133         this.originalValue = this.getValue();
40134         
40135         if(this.validationEvent == 'keyup'){
40136             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40137             this.inputEl().on('keyup', this.filterValidation, this);
40138         }
40139         else if(this.validationEvent !== false){
40140             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40141         }
40142         
40143         if(this.selectOnFocus){
40144             this.on("focus", this.preFocus, this);
40145             
40146         }
40147         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40148             this.inputEl().on("keypress", this.filterKeys, this);
40149         } else {
40150             this.inputEl().relayEvent('keypress', this);
40151         }
40152         
40153         var allowed = "0123456789";
40154         
40155         if(this.allowDecimals){
40156             allowed += this.decimalSeparator;
40157         }
40158         
40159         if(this.allowNegative){
40160             allowed += "-";
40161         }
40162         
40163         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40164         
40165         var keyPress = function(e){
40166             
40167             var k = e.getKey();
40168             
40169             var c = e.getCharCode();
40170             
40171             if(
40172                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40173                     allowed.indexOf(String.fromCharCode(c)) === -1
40174             ){
40175                 e.stopEvent();
40176                 return;
40177             }
40178             
40179             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40180                 return;
40181             }
40182             
40183             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40184                 e.stopEvent();
40185             }
40186         };
40187         
40188         this.inputEl().on("keypress", keyPress, this);
40189         
40190     },
40191     
40192     onTriggerClick : function(e)
40193     {   
40194         if(this.disabled){
40195             return;
40196         }
40197         
40198         this.page = 0;
40199         this.loadNext = false;
40200         
40201         if(this.isExpanded()){
40202             this.collapse();
40203             return;
40204         }
40205         
40206         this.hasFocus = true;
40207         
40208         if(this.triggerAction == 'all') {
40209             this.doQuery(this.allQuery, true);
40210             return;
40211         }
40212         
40213         this.doQuery(this.getRawValue());
40214     },
40215     
40216     getCurrency : function()
40217     {   
40218         var v = this.currencyEl().getValue();
40219         
40220         return v;
40221     },
40222     
40223     restrictHeight : function()
40224     {
40225         this.list.alignTo(this.currencyEl(), this.listAlign);
40226         this.list.alignTo(this.currencyEl(), this.listAlign);
40227     },
40228     
40229     onViewClick : function(view, doFocus, el, e)
40230     {
40231         var index = this.view.getSelectedIndexes()[0];
40232         
40233         var r = this.store.getAt(index);
40234         
40235         if(r){
40236             this.onSelect(r, index);
40237         }
40238     },
40239     
40240     onSelect : function(record, index){
40241         
40242         if(this.fireEvent('beforeselect', this, record, index) !== false){
40243         
40244             this.setFromCurrencyData(index > -1 ? record.data : false);
40245             
40246             this.collapse();
40247             
40248             this.fireEvent('select', this, record, index);
40249         }
40250     },
40251     
40252     setFromCurrencyData : function(o)
40253     {
40254         var currency = '';
40255         
40256         this.lastCurrency = o;
40257         
40258         if (this.currencyField) {
40259             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40260         } else {
40261             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40262         }
40263         
40264         this.lastSelectionText = currency;
40265         
40266         this.setCurrency(currency);
40267     },
40268     
40269     setFromData : function(o)
40270     {
40271         var c = {};
40272         
40273         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40274         
40275         this.setFromCurrencyData(c);
40276         
40277         var value = '';
40278         
40279         if (this.name) {
40280             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40281         } else {
40282             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40283         }
40284         
40285         this.setValue(value);
40286         
40287     },
40288     
40289     setCurrency : function(v)
40290     {   
40291         this.currencyValue = v;
40292         
40293         if(this.rendered){
40294             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40295             this.validate();
40296         }
40297     },
40298     
40299     setValue : function(v)
40300     {
40301         v = this.fixPrecision(v);
40302         
40303         v = String(v).replace(".", this.decimalSeparator);
40304         
40305         this.value = v;
40306         
40307         if(this.rendered){
40308             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40309             this.validate();
40310         }
40311     },
40312     
40313     getRawValue : function()
40314     {
40315         var v = this.inputEl().getValue();
40316         
40317         return v;
40318     },
40319     
40320     getValue : function()
40321     {
40322         return this.fixPrecision(this.parseValue(this.getRawValue()));
40323     },
40324     
40325     parseValue : function(value)
40326     {
40327         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40328         return isNaN(value) ? '' : value;
40329     },
40330     
40331     fixPrecision : function(value)
40332     {
40333         var nan = isNaN(value);
40334         
40335         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40336             return nan ? '' : value;
40337         }
40338         
40339         return parseFloat(value).toFixed(this.decimalPrecision);
40340     },
40341     
40342     decimalPrecisionFcn : function(v)
40343     {
40344         return Math.floor(v);
40345     },
40346     
40347     validateValue : function(value)
40348     {
40349         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40350             return false;
40351         }
40352         
40353         var num = this.parseValue(value);
40354         
40355         if(isNaN(num)){
40356             this.markInvalid(String.format(this.nanText, value));
40357             return false;
40358         }
40359         
40360         if(num < this.minValue){
40361             this.markInvalid(String.format(this.minText, this.minValue));
40362             return false;
40363         }
40364         
40365         if(num > this.maxValue){
40366             this.markInvalid(String.format(this.maxText, this.maxValue));
40367             return false;
40368         }
40369         
40370         return true;
40371     },
40372     
40373     validate : function()
40374     {
40375         if(this.disabled){
40376             this.markValid();
40377             return true;
40378         }
40379         
40380         var currency = this.getCurrency();
40381         
40382         if(this.validateValue(this.getRawValue()) && currency.length){
40383             this.markValid();
40384             return true;
40385         }
40386         
40387         this.markInvalid();
40388         return false;
40389     },
40390     
40391     getName: function()
40392     {
40393         return this.name;
40394     },
40395     
40396     beforeBlur : function()
40397     {
40398         if(!this.castInt){
40399             return;
40400         }
40401         
40402         var v = this.parseValue(this.getRawValue());
40403         
40404         if(v){
40405             this.setValue(v);
40406         }
40407     },
40408     
40409     onBlur : function()
40410     {
40411         this.beforeBlur();
40412         
40413         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40414             //this.el.removeClass(this.focusClass);
40415         }
40416         
40417         this.hasFocus = false;
40418         
40419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40420             this.validate();
40421         }
40422         
40423         var v = this.getValue();
40424         
40425         if(String(v) !== String(this.startValue)){
40426             this.fireEvent('change', this, v, this.startValue);
40427         }
40428         
40429         this.fireEvent("blur", this);
40430     },
40431     
40432     onCurrencyKeyUp : function(e)
40433     {
40434         if(!e.isSpecialKey()){
40435             this.hasFocus = true;
40436             this.lastKey = e.getKey();
40437             this.dqTask.delay(this.queryDelay);
40438         }
40439     },
40440     
40441     inputEl : function()
40442     {
40443         return this.el.select('.roo-money-amount-input', true).first();
40444     },
40445     
40446     currencyEl : function()
40447     {
40448         return this.el.select('.roo-money-currency-input', true).first();
40449     },
40450     
40451     initQuery : function()
40452     {
40453         var v = this.getCurrency();
40454         
40455         this.doQuery(v);
40456     },
40457     
40458     onTypeAhead : function()
40459     {
40460         if(this.store.getCount() > 0){
40461             var r = this.store.getAt(0);
40462             var newValue = r.data[this.currencyField];
40463             var len = newValue.length;
40464             var selStart = this.getCurrency().length;
40465             
40466             if(selStart != len){
40467                 this.setCurrency(newValue);
40468                 this.selectText(selStart, newValue.length);
40469             }
40470         }
40471     },
40472     
40473     selectText : function(start, end)
40474     {
40475         var v = this.getCurrency();
40476         
40477         if(v.length > 0){
40478             start = start === undefined ? 0 : start;
40479             end = end === undefined ? v.length : end;
40480             var d = this.el.dom;
40481             if(d.setSelectionRange){
40482                 d.setSelectionRange(start, end);
40483             }else if(d.createTextRange){
40484                 var range = d.createTextRange();
40485                 range.moveStart("character", start);
40486                 range.moveEnd("character", v.length-end);
40487                 range.select();
40488             }
40489         }
40490     }
40491     
40492 });