css-bootstrap/font-awesome-convert.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 (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     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         if(!this.rendered){
9362             return;
9363         }
9364         
9365         this.fieldLabel = v;
9366         
9367         if(this.indicator){
9368             var ar = this.el.select('label > span',true);
9369             if (!ar.length) {
9370                 Roo.log("could not find label > span on element");
9371                 Roo.log(this);
9372                 return;
9373             }
9374             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9375             return;
9376         }
9377         
9378         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9379     }
9380 });
9381
9382  
9383 /*
9384  * - LGPL
9385  *
9386  * Input
9387  * 
9388  */
9389
9390 /**
9391  * @class Roo.bootstrap.TextArea
9392  * @extends Roo.bootstrap.Input
9393  * Bootstrap TextArea class
9394  * @cfg {Number} cols Specifies the visible width of a text area
9395  * @cfg {Number} rows Specifies the visible number of lines in a text area
9396  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9397  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9398  * @cfg {string} html text
9399  * 
9400  * @constructor
9401  * Create a new TextArea
9402  * @param {Object} config The config object
9403  */
9404
9405 Roo.bootstrap.TextArea = function(config){
9406     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9407    
9408 };
9409
9410 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9411      
9412     cols : false,
9413     rows : 5,
9414     readOnly : false,
9415     warp : 'soft',
9416     resize : false,
9417     value: false,
9418     html: false,
9419     
9420     getAutoCreate : function(){
9421         
9422         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9423         
9424         var id = Roo.id();
9425         
9426         var cfg = {};
9427         
9428         if(this.inputType != 'hidden'){
9429             cfg.cls = 'form-group' //input-group
9430         }
9431         
9432         var input =  {
9433             tag: 'textarea',
9434             id : id,
9435             warp : this.warp,
9436             rows : this.rows,
9437             value : this.value || '',
9438             html: this.html || '',
9439             cls : 'form-control',
9440             placeholder : this.placeholder || '' 
9441             
9442         };
9443         
9444         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9445             input.maxLength = this.maxLength;
9446         }
9447         
9448         if(this.resize){
9449             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9450         }
9451         
9452         if(this.cols){
9453             input.cols = this.cols;
9454         }
9455         
9456         if (this.readOnly) {
9457             input.readonly = true;
9458         }
9459         
9460         if (this.name) {
9461             input.name = this.name;
9462         }
9463         
9464         if (this.size) {
9465             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9466         }
9467         
9468         var settings=this;
9469         ['xs','sm','md','lg'].map(function(size){
9470             if (settings[size]) {
9471                 cfg.cls += ' col-' + size + '-' + settings[size];
9472             }
9473         });
9474         
9475         var inputblock = input;
9476         
9477         if(this.hasFeedback && !this.allowBlank){
9478             
9479             var feedback = {
9480                 tag: 'span',
9481                 cls: 'glyphicon form-control-feedback'
9482             };
9483
9484             inputblock = {
9485                 cls : 'has-feedback',
9486                 cn :  [
9487                     input,
9488                     feedback
9489                 ] 
9490             };  
9491         }
9492         
9493         
9494         if (this.before || this.after) {
9495             
9496             inputblock = {
9497                 cls : 'input-group',
9498                 cn :  [] 
9499             };
9500             if (this.before) {
9501                 inputblock.cn.push({
9502                     tag :'span',
9503                     cls : 'input-group-addon',
9504                     html : this.before
9505                 });
9506             }
9507             
9508             inputblock.cn.push(input);
9509             
9510             if(this.hasFeedback && !this.allowBlank){
9511                 inputblock.cls += ' has-feedback';
9512                 inputblock.cn.push(feedback);
9513             }
9514             
9515             if (this.after) {
9516                 inputblock.cn.push({
9517                     tag :'span',
9518                     cls : 'input-group-addon',
9519                     html : this.after
9520                 });
9521             }
9522             
9523         }
9524         
9525         if (align ==='left' && this.fieldLabel.length) {
9526             cfg.cn = [
9527                 {
9528                     tag: 'label',
9529                     'for' :  id,
9530                     cls : 'control-label',
9531                     html : this.fieldLabel
9532                 },
9533                 {
9534                     cls : "",
9535                     cn: [
9536                         inputblock
9537                     ]
9538                 }
9539
9540             ];
9541             
9542             if(this.labelWidth > 12){
9543                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9544             }
9545
9546             if(this.labelWidth < 13 && this.labelmd == 0){
9547                 this.labelmd = this.labelWidth;
9548             }
9549
9550             if(this.labellg > 0){
9551                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9552                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9553             }
9554
9555             if(this.labelmd > 0){
9556                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9557                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9558             }
9559
9560             if(this.labelsm > 0){
9561                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9562                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9563             }
9564
9565             if(this.labelxs > 0){
9566                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9567                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9568             }
9569             
9570         } else if ( this.fieldLabel.length) {
9571             cfg.cn = [
9572
9573                {
9574                    tag: 'label',
9575                    //cls : 'input-group-addon',
9576                    html : this.fieldLabel
9577
9578                },
9579
9580                inputblock
9581
9582            ];
9583
9584         } else {
9585
9586             cfg.cn = [
9587
9588                 inputblock
9589
9590             ];
9591                 
9592         }
9593         
9594         if (this.disabled) {
9595             input.disabled=true;
9596         }
9597         
9598         return cfg;
9599         
9600     },
9601     /**
9602      * return the real textarea element.
9603      */
9604     inputEl: function ()
9605     {
9606         return this.el.select('textarea.form-control',true).first();
9607     },
9608     
9609     /**
9610      * Clear any invalid styles/messages for this field
9611      */
9612     clearInvalid : function()
9613     {
9614         
9615         if(!this.el || this.preventMark){ // not rendered
9616             return;
9617         }
9618         
9619         var label = this.el.select('label', true).first();
9620         var icon = this.el.select('i.fa-star', true).first();
9621         
9622         if(label && icon){
9623             icon.remove();
9624         }
9625         
9626         this.el.removeClass(this.invalidClass);
9627         
9628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9629             
9630             var feedback = this.el.select('.form-control-feedback', true).first();
9631             
9632             if(feedback){
9633                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9634             }
9635             
9636         }
9637         
9638         this.fireEvent('valid', this);
9639     },
9640     
9641      /**
9642      * Mark this field as valid
9643      */
9644     markValid : function()
9645     {
9646         if(!this.el  || this.preventMark){ // not rendered
9647             return;
9648         }
9649         
9650         this.el.removeClass([this.invalidClass, this.validClass]);
9651         
9652         var feedback = this.el.select('.form-control-feedback', true).first();
9653             
9654         if(feedback){
9655             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9656         }
9657
9658         if(this.disabled || this.allowBlank){
9659             return;
9660         }
9661         
9662         var label = this.el.select('label', true).first();
9663         var icon = this.el.select('i.fa-star', true).first();
9664         
9665         if(label && icon){
9666             icon.remove();
9667         }
9668         
9669         this.el.addClass(this.validClass);
9670         
9671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9672             
9673             var feedback = this.el.select('.form-control-feedback', true).first();
9674             
9675             if(feedback){
9676                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9677                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9678             }
9679             
9680         }
9681         
9682         this.fireEvent('valid', this);
9683     },
9684     
9685      /**
9686      * Mark this field as invalid
9687      * @param {String} msg The validation message
9688      */
9689     markInvalid : function(msg)
9690     {
9691         if(!this.el  || this.preventMark){ // not rendered
9692             return;
9693         }
9694         
9695         this.el.removeClass([this.invalidClass, this.validClass]);
9696         
9697         var feedback = this.el.select('.form-control-feedback', true).first();
9698             
9699         if(feedback){
9700             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9701         }
9702
9703         if(this.disabled || this.allowBlank){
9704             return;
9705         }
9706         
9707         var label = this.el.select('label', true).first();
9708         var icon = this.el.select('i.fa-star', true).first();
9709         
9710         if(!this.getValue().length && label && !icon){
9711             this.el.createChild({
9712                 tag : 'i',
9713                 cls : 'text-danger fa fa-lg fa-star',
9714                 tooltip : 'This field is required',
9715                 style : 'margin-right:5px;'
9716             }, label, true);
9717         }
9718
9719         this.el.addClass(this.invalidClass);
9720         
9721         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9722             
9723             var feedback = this.el.select('.form-control-feedback', true).first();
9724             
9725             if(feedback){
9726                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9727                 
9728                 if(this.getValue().length || this.forceFeedback){
9729                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9730                 }
9731                 
9732             }
9733             
9734         }
9735         
9736         this.fireEvent('invalid', this, msg);
9737     }
9738 });
9739
9740  
9741 /*
9742  * - LGPL
9743  *
9744  * trigger field - base class for combo..
9745  * 
9746  */
9747  
9748 /**
9749  * @class Roo.bootstrap.TriggerField
9750  * @extends Roo.bootstrap.Input
9751  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9752  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9753  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9754  * for which you can provide a custom implementation.  For example:
9755  * <pre><code>
9756 var trigger = new Roo.bootstrap.TriggerField();
9757 trigger.onTriggerClick = myTriggerFn;
9758 trigger.applyTo('my-field');
9759 </code></pre>
9760  *
9761  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9762  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9763  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9764  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9765  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9766
9767  * @constructor
9768  * Create a new TriggerField.
9769  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9770  * to the base TextField)
9771  */
9772 Roo.bootstrap.TriggerField = function(config){
9773     this.mimicing = false;
9774     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9775 };
9776
9777 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9778     /**
9779      * @cfg {String} triggerClass A CSS class to apply to the trigger
9780      */
9781      /**
9782      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9783      */
9784     hideTrigger:false,
9785
9786     /**
9787      * @cfg {Boolean} removable (true|false) special filter default false
9788      */
9789     removable : false,
9790     
9791     /** @cfg {Boolean} grow @hide */
9792     /** @cfg {Number} growMin @hide */
9793     /** @cfg {Number} growMax @hide */
9794
9795     /**
9796      * @hide 
9797      * @method
9798      */
9799     autoSize: Roo.emptyFn,
9800     // private
9801     monitorTab : true,
9802     // private
9803     deferHeight : true,
9804
9805     
9806     actionMode : 'wrap',
9807     
9808     caret : false,
9809     
9810     
9811     getAutoCreate : function(){
9812        
9813         var align = this.labelAlign || this.parentLabelAlign();
9814         
9815         var id = Roo.id();
9816         
9817         var cfg = {
9818             cls: 'form-group' //input-group
9819         };
9820         
9821         
9822         var input =  {
9823             tag: 'input',
9824             id : id,
9825             type : this.inputType,
9826             cls : 'form-control',
9827             autocomplete: 'new-password',
9828             placeholder : this.placeholder || '' 
9829             
9830         };
9831         if (this.name) {
9832             input.name = this.name;
9833         }
9834         if (this.size) {
9835             input.cls += ' input-' + this.size;
9836         }
9837         
9838         if (this.disabled) {
9839             input.disabled=true;
9840         }
9841         
9842         var inputblock = input;
9843         
9844         if(this.hasFeedback && !this.allowBlank){
9845             
9846             var feedback = {
9847                 tag: 'span',
9848                 cls: 'glyphicon form-control-feedback'
9849             };
9850             
9851             if(this.removable && !this.editable && !this.tickable){
9852                 inputblock = {
9853                     cls : 'has-feedback',
9854                     cn :  [
9855                         inputblock,
9856                         {
9857                             tag: 'button',
9858                             html : 'x',
9859                             cls : 'roo-combo-removable-btn close'
9860                         },
9861                         feedback
9862                     ] 
9863                 };
9864             } else {
9865                 inputblock = {
9866                     cls : 'has-feedback',
9867                     cn :  [
9868                         inputblock,
9869                         feedback
9870                     ] 
9871                 };
9872             }
9873
9874         } else {
9875             if(this.removable && !this.editable && !this.tickable){
9876                 inputblock = {
9877                     cls : 'roo-removable',
9878                     cn :  [
9879                         inputblock,
9880                         {
9881                             tag: 'button',
9882                             html : 'x',
9883                             cls : 'roo-combo-removable-btn close'
9884                         }
9885                     ] 
9886                 };
9887             }
9888         }
9889         
9890         if (this.before || this.after) {
9891             
9892             inputblock = {
9893                 cls : 'input-group',
9894                 cn :  [] 
9895             };
9896             if (this.before) {
9897                 inputblock.cn.push({
9898                     tag :'span',
9899                     cls : 'input-group-addon',
9900                     html : this.before
9901                 });
9902             }
9903             
9904             inputblock.cn.push(input);
9905             
9906             if(this.hasFeedback && !this.allowBlank){
9907                 inputblock.cls += ' has-feedback';
9908                 inputblock.cn.push(feedback);
9909             }
9910             
9911             if (this.after) {
9912                 inputblock.cn.push({
9913                     tag :'span',
9914                     cls : 'input-group-addon',
9915                     html : this.after
9916                 });
9917             }
9918             
9919         };
9920         
9921         var box = {
9922             tag: 'div',
9923             cn: [
9924                 {
9925                     tag: 'input',
9926                     type : 'hidden',
9927                     cls: 'form-hidden-field'
9928                 },
9929                 inputblock
9930             ]
9931             
9932         };
9933         
9934         if(this.multiple){
9935             box = {
9936                 tag: 'div',
9937                 cn: [
9938                     {
9939                         tag: 'input',
9940                         type : 'hidden',
9941                         cls: 'form-hidden-field'
9942                     },
9943                     {
9944                         tag: 'ul',
9945                         cls: 'roo-select2-choices',
9946                         cn:[
9947                             {
9948                                 tag: 'li',
9949                                 cls: 'roo-select2-search-field',
9950                                 cn: [
9951
9952                                     inputblock
9953                                 ]
9954                             }
9955                         ]
9956                     }
9957                 ]
9958             }
9959         };
9960         
9961         var combobox = {
9962             cls: 'roo-select2-container input-group',
9963             cn: [
9964                 box
9965 //                {
9966 //                    tag: 'ul',
9967 //                    cls: 'typeahead typeahead-long dropdown-menu',
9968 //                    style: 'display:none'
9969 //                }
9970             ]
9971         };
9972         
9973         if(!this.multiple && this.showToggleBtn){
9974             
9975             var caret = {
9976                         tag: 'span',
9977                         cls: 'caret'
9978              };
9979             if (this.caret != false) {
9980                 caret = {
9981                      tag: 'i',
9982                      cls: 'fa fa-' + this.caret
9983                 };
9984                 
9985             }
9986             
9987             combobox.cn.push({
9988                 tag :'span',
9989                 cls : 'input-group-addon btn dropdown-toggle',
9990                 cn : [
9991                     caret,
9992                     {
9993                         tag: 'span',
9994                         cls: 'combobox-clear',
9995                         cn  : [
9996                             {
9997                                 tag : 'i',
9998                                 cls: 'icon-remove'
9999                             }
10000                         ]
10001                     }
10002                 ]
10003
10004             })
10005         }
10006         
10007         if(this.multiple){
10008             combobox.cls += ' roo-select2-container-multi';
10009         }
10010         
10011         if (align ==='left' && this.fieldLabel.length) {
10012             
10013             cfg.cls += ' roo-form-group-label-left';
10014
10015             cfg.cn = [
10016                 {
10017                     tag : 'i',
10018                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10019                     tooltip : 'This field is required'
10020                 },
10021                 {
10022                     tag: 'label',
10023                     'for' :  id,
10024                     cls : 'control-label',
10025                     html : this.fieldLabel
10026
10027                 },
10028                 {
10029                     cls : "", 
10030                     cn: [
10031                         combobox
10032                     ]
10033                 }
10034
10035             ];
10036             
10037             var labelCfg = cfg.cn[1];
10038             var contentCfg = cfg.cn[2];
10039             
10040             if(this.indicatorpos == 'right'){
10041                 cfg.cn = [
10042                     {
10043                         tag: 'label',
10044                         'for' :  id,
10045                         cls : 'control-label',
10046                         cn : [
10047                             {
10048                                 tag : 'span',
10049                                 html : this.fieldLabel
10050                             },
10051                             {
10052                                 tag : 'i',
10053                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10054                                 tooltip : 'This field is required'
10055                             }
10056                         ]
10057                     },
10058                     {
10059                         cls : "", 
10060                         cn: [
10061                             combobox
10062                         ]
10063                     }
10064
10065                 ];
10066                 
10067                 labelCfg = cfg.cn[0];
10068                 contentCfg = cfg.cn[1];
10069             }
10070             
10071             if(this.labelWidth > 12){
10072                 labelCfg.style = "width: " + this.labelWidth + 'px';
10073             }
10074             
10075             if(this.labelWidth < 13 && this.labelmd == 0){
10076                 this.labelmd = this.labelWidth;
10077             }
10078             
10079             if(this.labellg > 0){
10080                 labelCfg.cls += ' col-lg-' + this.labellg;
10081                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10082             }
10083             
10084             if(this.labelmd > 0){
10085                 labelCfg.cls += ' col-md-' + this.labelmd;
10086                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10087             }
10088             
10089             if(this.labelsm > 0){
10090                 labelCfg.cls += ' col-sm-' + this.labelsm;
10091                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10092             }
10093             
10094             if(this.labelxs > 0){
10095                 labelCfg.cls += ' col-xs-' + this.labelxs;
10096                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10097             }
10098             
10099         } else if ( this.fieldLabel.length) {
10100 //                Roo.log(" label");
10101             cfg.cn = [
10102                 {
10103                    tag : 'i',
10104                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10105                    tooltip : 'This field is required'
10106                },
10107                {
10108                    tag: 'label',
10109                    //cls : 'input-group-addon',
10110                    html : this.fieldLabel
10111
10112                },
10113
10114                combobox
10115
10116             ];
10117             
10118             if(this.indicatorpos == 'right'){
10119                 
10120                 cfg.cn = [
10121                     {
10122                        tag: 'label',
10123                        cn : [
10124                            {
10125                                tag : 'span',
10126                                html : this.fieldLabel
10127                            },
10128                            {
10129                               tag : 'i',
10130                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10131                               tooltip : 'This field is required'
10132                            }
10133                        ]
10134
10135                     },
10136                     combobox
10137
10138                 ];
10139
10140             }
10141
10142         } else {
10143             
10144 //                Roo.log(" no label && no align");
10145                 cfg = combobox
10146                      
10147                 
10148         }
10149         
10150         var settings=this;
10151         ['xs','sm','md','lg'].map(function(size){
10152             if (settings[size]) {
10153                 cfg.cls += ' col-' + size + '-' + settings[size];
10154             }
10155         });
10156         
10157         return cfg;
10158         
10159     },
10160     
10161     
10162     
10163     // private
10164     onResize : function(w, h){
10165 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10166 //        if(typeof w == 'number'){
10167 //            var x = w - this.trigger.getWidth();
10168 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10169 //            this.trigger.setStyle('left', x+'px');
10170 //        }
10171     },
10172
10173     // private
10174     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10175
10176     // private
10177     getResizeEl : function(){
10178         return this.inputEl();
10179     },
10180
10181     // private
10182     getPositionEl : function(){
10183         return this.inputEl();
10184     },
10185
10186     // private
10187     alignErrorIcon : function(){
10188         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10189     },
10190
10191     // private
10192     initEvents : function(){
10193         
10194         this.createList();
10195         
10196         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10197         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10198         if(!this.multiple && this.showToggleBtn){
10199             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10200             if(this.hideTrigger){
10201                 this.trigger.setDisplayed(false);
10202             }
10203             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10204         }
10205         
10206         if(this.multiple){
10207             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10208         }
10209         
10210         if(this.removable && !this.editable && !this.tickable){
10211             var close = this.closeTriggerEl();
10212             
10213             if(close){
10214                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10215                 close.on('click', this.removeBtnClick, this, close);
10216             }
10217         }
10218         
10219         //this.trigger.addClassOnOver('x-form-trigger-over');
10220         //this.trigger.addClassOnClick('x-form-trigger-click');
10221         
10222         //if(!this.width){
10223         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10224         //}
10225     },
10226     
10227     closeTriggerEl : function()
10228     {
10229         var close = this.el.select('.roo-combo-removable-btn', true).first();
10230         return close ? close : false;
10231     },
10232     
10233     removeBtnClick : function(e, h, el)
10234     {
10235         e.preventDefault();
10236         
10237         if(this.fireEvent("remove", this) !== false){
10238             this.reset();
10239             this.fireEvent("afterremove", this)
10240         }
10241     },
10242     
10243     createList : function()
10244     {
10245         this.list = Roo.get(document.body).createChild({
10246             tag: 'ul',
10247             cls: 'typeahead typeahead-long dropdown-menu',
10248             style: 'display:none'
10249         });
10250         
10251         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10252         
10253     },
10254
10255     // private
10256     initTrigger : function(){
10257        
10258     },
10259
10260     // private
10261     onDestroy : function(){
10262         if(this.trigger){
10263             this.trigger.removeAllListeners();
10264           //  this.trigger.remove();
10265         }
10266         //if(this.wrap){
10267         //    this.wrap.remove();
10268         //}
10269         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10270     },
10271
10272     // private
10273     onFocus : function(){
10274         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10275         /*
10276         if(!this.mimicing){
10277             this.wrap.addClass('x-trigger-wrap-focus');
10278             this.mimicing = true;
10279             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10280             if(this.monitorTab){
10281                 this.el.on("keydown", this.checkTab, this);
10282             }
10283         }
10284         */
10285     },
10286
10287     // private
10288     checkTab : function(e){
10289         if(e.getKey() == e.TAB){
10290             this.triggerBlur();
10291         }
10292     },
10293
10294     // private
10295     onBlur : function(){
10296         // do nothing
10297     },
10298
10299     // private
10300     mimicBlur : function(e, t){
10301         /*
10302         if(!this.wrap.contains(t) && this.validateBlur()){
10303             this.triggerBlur();
10304         }
10305         */
10306     },
10307
10308     // private
10309     triggerBlur : function(){
10310         this.mimicing = false;
10311         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10312         if(this.monitorTab){
10313             this.el.un("keydown", this.checkTab, this);
10314         }
10315         //this.wrap.removeClass('x-trigger-wrap-focus');
10316         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10317     },
10318
10319     // private
10320     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10321     validateBlur : function(e, t){
10322         return true;
10323     },
10324
10325     // private
10326     onDisable : function(){
10327         this.inputEl().dom.disabled = true;
10328         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10329         //if(this.wrap){
10330         //    this.wrap.addClass('x-item-disabled');
10331         //}
10332     },
10333
10334     // private
10335     onEnable : function(){
10336         this.inputEl().dom.disabled = false;
10337         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10338         //if(this.wrap){
10339         //    this.el.removeClass('x-item-disabled');
10340         //}
10341     },
10342
10343     // private
10344     onShow : function(){
10345         var ae = this.getActionEl();
10346         
10347         if(ae){
10348             ae.dom.style.display = '';
10349             ae.dom.style.visibility = 'visible';
10350         }
10351     },
10352
10353     // private
10354     
10355     onHide : function(){
10356         var ae = this.getActionEl();
10357         ae.dom.style.display = 'none';
10358     },
10359
10360     /**
10361      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10362      * by an implementing function.
10363      * @method
10364      * @param {EventObject} e
10365      */
10366     onTriggerClick : Roo.emptyFn
10367 });
10368  /*
10369  * Based on:
10370  * Ext JS Library 1.1.1
10371  * Copyright(c) 2006-2007, Ext JS, LLC.
10372  *
10373  * Originally Released Under LGPL - original licence link has changed is not relivant.
10374  *
10375  * Fork - LGPL
10376  * <script type="text/javascript">
10377  */
10378
10379
10380 /**
10381  * @class Roo.data.SortTypes
10382  * @singleton
10383  * Defines the default sorting (casting?) comparison functions used when sorting data.
10384  */
10385 Roo.data.SortTypes = {
10386     /**
10387      * Default sort that does nothing
10388      * @param {Mixed} s The value being converted
10389      * @return {Mixed} The comparison value
10390      */
10391     none : function(s){
10392         return s;
10393     },
10394     
10395     /**
10396      * The regular expression used to strip tags
10397      * @type {RegExp}
10398      * @property
10399      */
10400     stripTagsRE : /<\/?[^>]+>/gi,
10401     
10402     /**
10403      * Strips all HTML tags to sort on text only
10404      * @param {Mixed} s The value being converted
10405      * @return {String} The comparison value
10406      */
10407     asText : function(s){
10408         return String(s).replace(this.stripTagsRE, "");
10409     },
10410     
10411     /**
10412      * Strips all HTML tags to sort on text only - Case insensitive
10413      * @param {Mixed} s The value being converted
10414      * @return {String} The comparison value
10415      */
10416     asUCText : function(s){
10417         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10418     },
10419     
10420     /**
10421      * Case insensitive string
10422      * @param {Mixed} s The value being converted
10423      * @return {String} The comparison value
10424      */
10425     asUCString : function(s) {
10426         return String(s).toUpperCase();
10427     },
10428     
10429     /**
10430      * Date sorting
10431      * @param {Mixed} s The value being converted
10432      * @return {Number} The comparison value
10433      */
10434     asDate : function(s) {
10435         if(!s){
10436             return 0;
10437         }
10438         if(s instanceof Date){
10439             return s.getTime();
10440         }
10441         return Date.parse(String(s));
10442     },
10443     
10444     /**
10445      * Float sorting
10446      * @param {Mixed} s The value being converted
10447      * @return {Float} The comparison value
10448      */
10449     asFloat : function(s) {
10450         var val = parseFloat(String(s).replace(/,/g, ""));
10451         if(isNaN(val)) {
10452             val = 0;
10453         }
10454         return val;
10455     },
10456     
10457     /**
10458      * Integer sorting
10459      * @param {Mixed} s The value being converted
10460      * @return {Number} The comparison value
10461      */
10462     asInt : function(s) {
10463         var val = parseInt(String(s).replace(/,/g, ""));
10464         if(isNaN(val)) {
10465             val = 0;
10466         }
10467         return val;
10468     }
10469 };/*
10470  * Based on:
10471  * Ext JS Library 1.1.1
10472  * Copyright(c) 2006-2007, Ext JS, LLC.
10473  *
10474  * Originally Released Under LGPL - original licence link has changed is not relivant.
10475  *
10476  * Fork - LGPL
10477  * <script type="text/javascript">
10478  */
10479
10480 /**
10481 * @class Roo.data.Record
10482  * Instances of this class encapsulate both record <em>definition</em> information, and record
10483  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10484  * to access Records cached in an {@link Roo.data.Store} object.<br>
10485  * <p>
10486  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10487  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10488  * objects.<br>
10489  * <p>
10490  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10491  * @constructor
10492  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10493  * {@link #create}. The parameters are the same.
10494  * @param {Array} data An associative Array of data values keyed by the field name.
10495  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10496  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10497  * not specified an integer id is generated.
10498  */
10499 Roo.data.Record = function(data, id){
10500     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10501     this.data = data;
10502 };
10503
10504 /**
10505  * Generate a constructor for a specific record layout.
10506  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10507  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10508  * Each field definition object may contain the following properties: <ul>
10509  * <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,
10510  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10511  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10512  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10513  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10514  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10515  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10516  * this may be omitted.</p></li>
10517  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10518  * <ul><li>auto (Default, implies no conversion)</li>
10519  * <li>string</li>
10520  * <li>int</li>
10521  * <li>float</li>
10522  * <li>boolean</li>
10523  * <li>date</li></ul></p></li>
10524  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10525  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10526  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10527  * by the Reader into an object that will be stored in the Record. It is passed the
10528  * following parameters:<ul>
10529  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10530  * </ul></p></li>
10531  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10532  * </ul>
10533  * <br>usage:<br><pre><code>
10534 var TopicRecord = Roo.data.Record.create(
10535     {name: 'title', mapping: 'topic_title'},
10536     {name: 'author', mapping: 'username'},
10537     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10538     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10539     {name: 'lastPoster', mapping: 'user2'},
10540     {name: 'excerpt', mapping: 'post_text'}
10541 );
10542
10543 var myNewRecord = new TopicRecord({
10544     title: 'Do my job please',
10545     author: 'noobie',
10546     totalPosts: 1,
10547     lastPost: new Date(),
10548     lastPoster: 'Animal',
10549     excerpt: 'No way dude!'
10550 });
10551 myStore.add(myNewRecord);
10552 </code></pre>
10553  * @method create
10554  * @static
10555  */
10556 Roo.data.Record.create = function(o){
10557     var f = function(){
10558         f.superclass.constructor.apply(this, arguments);
10559     };
10560     Roo.extend(f, Roo.data.Record);
10561     var p = f.prototype;
10562     p.fields = new Roo.util.MixedCollection(false, function(field){
10563         return field.name;
10564     });
10565     for(var i = 0, len = o.length; i < len; i++){
10566         p.fields.add(new Roo.data.Field(o[i]));
10567     }
10568     f.getField = function(name){
10569         return p.fields.get(name);  
10570     };
10571     return f;
10572 };
10573
10574 Roo.data.Record.AUTO_ID = 1000;
10575 Roo.data.Record.EDIT = 'edit';
10576 Roo.data.Record.REJECT = 'reject';
10577 Roo.data.Record.COMMIT = 'commit';
10578
10579 Roo.data.Record.prototype = {
10580     /**
10581      * Readonly flag - true if this record has been modified.
10582      * @type Boolean
10583      */
10584     dirty : false,
10585     editing : false,
10586     error: null,
10587     modified: null,
10588
10589     // private
10590     join : function(store){
10591         this.store = store;
10592     },
10593
10594     /**
10595      * Set the named field to the specified value.
10596      * @param {String} name The name of the field to set.
10597      * @param {Object} value The value to set the field to.
10598      */
10599     set : function(name, value){
10600         if(this.data[name] == value){
10601             return;
10602         }
10603         this.dirty = true;
10604         if(!this.modified){
10605             this.modified = {};
10606         }
10607         if(typeof this.modified[name] == 'undefined'){
10608             this.modified[name] = this.data[name];
10609         }
10610         this.data[name] = value;
10611         if(!this.editing && this.store){
10612             this.store.afterEdit(this);
10613         }       
10614     },
10615
10616     /**
10617      * Get the value of the named field.
10618      * @param {String} name The name of the field to get the value of.
10619      * @return {Object} The value of the field.
10620      */
10621     get : function(name){
10622         return this.data[name]; 
10623     },
10624
10625     // private
10626     beginEdit : function(){
10627         this.editing = true;
10628         this.modified = {}; 
10629     },
10630
10631     // private
10632     cancelEdit : function(){
10633         this.editing = false;
10634         delete this.modified;
10635     },
10636
10637     // private
10638     endEdit : function(){
10639         this.editing = false;
10640         if(this.dirty && this.store){
10641             this.store.afterEdit(this);
10642         }
10643     },
10644
10645     /**
10646      * Usually called by the {@link Roo.data.Store} which owns the Record.
10647      * Rejects all changes made to the Record since either creation, or the last commit operation.
10648      * Modified fields are reverted to their original values.
10649      * <p>
10650      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10651      * of reject operations.
10652      */
10653     reject : function(){
10654         var m = this.modified;
10655         for(var n in m){
10656             if(typeof m[n] != "function"){
10657                 this.data[n] = m[n];
10658             }
10659         }
10660         this.dirty = false;
10661         delete this.modified;
10662         this.editing = false;
10663         if(this.store){
10664             this.store.afterReject(this);
10665         }
10666     },
10667
10668     /**
10669      * Usually called by the {@link Roo.data.Store} which owns the Record.
10670      * Commits all changes made to the Record since either creation, or the last commit operation.
10671      * <p>
10672      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10673      * of commit operations.
10674      */
10675     commit : function(){
10676         this.dirty = false;
10677         delete this.modified;
10678         this.editing = false;
10679         if(this.store){
10680             this.store.afterCommit(this);
10681         }
10682     },
10683
10684     // private
10685     hasError : function(){
10686         return this.error != null;
10687     },
10688
10689     // private
10690     clearError : function(){
10691         this.error = null;
10692     },
10693
10694     /**
10695      * Creates a copy of this record.
10696      * @param {String} id (optional) A new record id if you don't want to use this record's id
10697      * @return {Record}
10698      */
10699     copy : function(newId) {
10700         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10701     }
10702 };/*
10703  * Based on:
10704  * Ext JS Library 1.1.1
10705  * Copyright(c) 2006-2007, Ext JS, LLC.
10706  *
10707  * Originally Released Under LGPL - original licence link has changed is not relivant.
10708  *
10709  * Fork - LGPL
10710  * <script type="text/javascript">
10711  */
10712
10713
10714
10715 /**
10716  * @class Roo.data.Store
10717  * @extends Roo.util.Observable
10718  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10719  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10720  * <p>
10721  * 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
10722  * has no knowledge of the format of the data returned by the Proxy.<br>
10723  * <p>
10724  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10725  * instances from the data object. These records are cached and made available through accessor functions.
10726  * @constructor
10727  * Creates a new Store.
10728  * @param {Object} config A config object containing the objects needed for the Store to access data,
10729  * and read the data into Records.
10730  */
10731 Roo.data.Store = function(config){
10732     this.data = new Roo.util.MixedCollection(false);
10733     this.data.getKey = function(o){
10734         return o.id;
10735     };
10736     this.baseParams = {};
10737     // private
10738     this.paramNames = {
10739         "start" : "start",
10740         "limit" : "limit",
10741         "sort" : "sort",
10742         "dir" : "dir",
10743         "multisort" : "_multisort"
10744     };
10745
10746     if(config && config.data){
10747         this.inlineData = config.data;
10748         delete config.data;
10749     }
10750
10751     Roo.apply(this, config);
10752     
10753     if(this.reader){ // reader passed
10754         this.reader = Roo.factory(this.reader, Roo.data);
10755         this.reader.xmodule = this.xmodule || false;
10756         if(!this.recordType){
10757             this.recordType = this.reader.recordType;
10758         }
10759         if(this.reader.onMetaChange){
10760             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10761         }
10762     }
10763
10764     if(this.recordType){
10765         this.fields = this.recordType.prototype.fields;
10766     }
10767     this.modified = [];
10768
10769     this.addEvents({
10770         /**
10771          * @event datachanged
10772          * Fires when the data cache has changed, and a widget which is using this Store
10773          * as a Record cache should refresh its view.
10774          * @param {Store} this
10775          */
10776         datachanged : true,
10777         /**
10778          * @event metachange
10779          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10780          * @param {Store} this
10781          * @param {Object} meta The JSON metadata
10782          */
10783         metachange : true,
10784         /**
10785          * @event add
10786          * Fires when Records have been added to the Store
10787          * @param {Store} this
10788          * @param {Roo.data.Record[]} records The array of Records added
10789          * @param {Number} index The index at which the record(s) were added
10790          */
10791         add : true,
10792         /**
10793          * @event remove
10794          * Fires when a Record has been removed from the Store
10795          * @param {Store} this
10796          * @param {Roo.data.Record} record The Record that was removed
10797          * @param {Number} index The index at which the record was removed
10798          */
10799         remove : true,
10800         /**
10801          * @event update
10802          * Fires when a Record has been updated
10803          * @param {Store} this
10804          * @param {Roo.data.Record} record The Record that was updated
10805          * @param {String} operation The update operation being performed.  Value may be one of:
10806          * <pre><code>
10807  Roo.data.Record.EDIT
10808  Roo.data.Record.REJECT
10809  Roo.data.Record.COMMIT
10810          * </code></pre>
10811          */
10812         update : true,
10813         /**
10814          * @event clear
10815          * Fires when the data cache has been cleared.
10816          * @param {Store} this
10817          */
10818         clear : true,
10819         /**
10820          * @event beforeload
10821          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10822          * the load action will be canceled.
10823          * @param {Store} this
10824          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10825          */
10826         beforeload : true,
10827         /**
10828          * @event beforeloadadd
10829          * Fires after a new set of Records has been loaded.
10830          * @param {Store} this
10831          * @param {Roo.data.Record[]} records The Records that were loaded
10832          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10833          */
10834         beforeloadadd : true,
10835         /**
10836          * @event load
10837          * Fires after a new set of Records has been loaded, before they are added to the store.
10838          * @param {Store} this
10839          * @param {Roo.data.Record[]} records The Records that were loaded
10840          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10841          * @params {Object} return from reader
10842          */
10843         load : true,
10844         /**
10845          * @event loadexception
10846          * Fires if an exception occurs in the Proxy during loading.
10847          * Called with the signature of the Proxy's "loadexception" event.
10848          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10849          * 
10850          * @param {Proxy} 
10851          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10852          * @param {Object} load options 
10853          * @param {Object} jsonData from your request (normally this contains the Exception)
10854          */
10855         loadexception : true
10856     });
10857     
10858     if(this.proxy){
10859         this.proxy = Roo.factory(this.proxy, Roo.data);
10860         this.proxy.xmodule = this.xmodule || false;
10861         this.relayEvents(this.proxy,  ["loadexception"]);
10862     }
10863     this.sortToggle = {};
10864     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10865
10866     Roo.data.Store.superclass.constructor.call(this);
10867
10868     if(this.inlineData){
10869         this.loadData(this.inlineData);
10870         delete this.inlineData;
10871     }
10872 };
10873
10874 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10875      /**
10876     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10877     * without a remote query - used by combo/forms at present.
10878     */
10879     
10880     /**
10881     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10882     */
10883     /**
10884     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10885     */
10886     /**
10887     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10888     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10889     */
10890     /**
10891     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10892     * on any HTTP request
10893     */
10894     /**
10895     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10896     */
10897     /**
10898     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10899     */
10900     multiSort: false,
10901     /**
10902     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10903     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10904     */
10905     remoteSort : false,
10906
10907     /**
10908     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10909      * loaded or when a record is removed. (defaults to false).
10910     */
10911     pruneModifiedRecords : false,
10912
10913     // private
10914     lastOptions : null,
10915
10916     /**
10917      * Add Records to the Store and fires the add event.
10918      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10919      */
10920     add : function(records){
10921         records = [].concat(records);
10922         for(var i = 0, len = records.length; i < len; i++){
10923             records[i].join(this);
10924         }
10925         var index = this.data.length;
10926         this.data.addAll(records);
10927         this.fireEvent("add", this, records, index);
10928     },
10929
10930     /**
10931      * Remove a Record from the Store and fires the remove event.
10932      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10933      */
10934     remove : function(record){
10935         var index = this.data.indexOf(record);
10936         this.data.removeAt(index);
10937         if(this.pruneModifiedRecords){
10938             this.modified.remove(record);
10939         }
10940         this.fireEvent("remove", this, record, index);
10941     },
10942
10943     /**
10944      * Remove all Records from the Store and fires the clear event.
10945      */
10946     removeAll : function(){
10947         this.data.clear();
10948         if(this.pruneModifiedRecords){
10949             this.modified = [];
10950         }
10951         this.fireEvent("clear", this);
10952     },
10953
10954     /**
10955      * Inserts Records to the Store at the given index and fires the add event.
10956      * @param {Number} index The start index at which to insert the passed Records.
10957      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10958      */
10959     insert : function(index, records){
10960         records = [].concat(records);
10961         for(var i = 0, len = records.length; i < len; i++){
10962             this.data.insert(index, records[i]);
10963             records[i].join(this);
10964         }
10965         this.fireEvent("add", this, records, index);
10966     },
10967
10968     /**
10969      * Get the index within the cache of the passed Record.
10970      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10971      * @return {Number} The index of the passed Record. Returns -1 if not found.
10972      */
10973     indexOf : function(record){
10974         return this.data.indexOf(record);
10975     },
10976
10977     /**
10978      * Get the index within the cache of the Record with the passed id.
10979      * @param {String} id The id of the Record to find.
10980      * @return {Number} The index of the Record. Returns -1 if not found.
10981      */
10982     indexOfId : function(id){
10983         return this.data.indexOfKey(id);
10984     },
10985
10986     /**
10987      * Get the Record with the specified id.
10988      * @param {String} id The id of the Record to find.
10989      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10990      */
10991     getById : function(id){
10992         return this.data.key(id);
10993     },
10994
10995     /**
10996      * Get the Record at the specified index.
10997      * @param {Number} index The index of the Record to find.
10998      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10999      */
11000     getAt : function(index){
11001         return this.data.itemAt(index);
11002     },
11003
11004     /**
11005      * Returns a range of Records between specified indices.
11006      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11007      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11008      * @return {Roo.data.Record[]} An array of Records
11009      */
11010     getRange : function(start, end){
11011         return this.data.getRange(start, end);
11012     },
11013
11014     // private
11015     storeOptions : function(o){
11016         o = Roo.apply({}, o);
11017         delete o.callback;
11018         delete o.scope;
11019         this.lastOptions = o;
11020     },
11021
11022     /**
11023      * Loads the Record cache from the configured Proxy using the configured Reader.
11024      * <p>
11025      * If using remote paging, then the first load call must specify the <em>start</em>
11026      * and <em>limit</em> properties in the options.params property to establish the initial
11027      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11028      * <p>
11029      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11030      * and this call will return before the new data has been loaded. Perform any post-processing
11031      * in a callback function, or in a "load" event handler.</strong>
11032      * <p>
11033      * @param {Object} options An object containing properties which control loading options:<ul>
11034      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11035      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11036      * passed the following arguments:<ul>
11037      * <li>r : Roo.data.Record[]</li>
11038      * <li>options: Options object from the load call</li>
11039      * <li>success: Boolean success indicator</li></ul></li>
11040      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11041      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11042      * </ul>
11043      */
11044     load : function(options){
11045         options = options || {};
11046         if(this.fireEvent("beforeload", this, options) !== false){
11047             this.storeOptions(options);
11048             var p = Roo.apply(options.params || {}, this.baseParams);
11049             // if meta was not loaded from remote source.. try requesting it.
11050             if (!this.reader.metaFromRemote) {
11051                 p._requestMeta = 1;
11052             }
11053             if(this.sortInfo && this.remoteSort){
11054                 var pn = this.paramNames;
11055                 p[pn["sort"]] = this.sortInfo.field;
11056                 p[pn["dir"]] = this.sortInfo.direction;
11057             }
11058             if (this.multiSort) {
11059                 var pn = this.paramNames;
11060                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11061             }
11062             
11063             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11064         }
11065     },
11066
11067     /**
11068      * Reloads the Record cache from the configured Proxy using the configured Reader and
11069      * the options from the last load operation performed.
11070      * @param {Object} options (optional) An object containing properties which may override the options
11071      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11072      * the most recently used options are reused).
11073      */
11074     reload : function(options){
11075         this.load(Roo.applyIf(options||{}, this.lastOptions));
11076     },
11077
11078     // private
11079     // Called as a callback by the Reader during a load operation.
11080     loadRecords : function(o, options, success){
11081         if(!o || success === false){
11082             if(success !== false){
11083                 this.fireEvent("load", this, [], options, o);
11084             }
11085             if(options.callback){
11086                 options.callback.call(options.scope || this, [], options, false);
11087             }
11088             return;
11089         }
11090         // if data returned failure - throw an exception.
11091         if (o.success === false) {
11092             // show a message if no listener is registered.
11093             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11094                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11095             }
11096             // loadmask wil be hooked into this..
11097             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11098             return;
11099         }
11100         var r = o.records, t = o.totalRecords || r.length;
11101         
11102         this.fireEvent("beforeloadadd", this, r, options, o);
11103         
11104         if(!options || options.add !== true){
11105             if(this.pruneModifiedRecords){
11106                 this.modified = [];
11107             }
11108             for(var i = 0, len = r.length; i < len; i++){
11109                 r[i].join(this);
11110             }
11111             if(this.snapshot){
11112                 this.data = this.snapshot;
11113                 delete this.snapshot;
11114             }
11115             this.data.clear();
11116             this.data.addAll(r);
11117             this.totalLength = t;
11118             this.applySort();
11119             this.fireEvent("datachanged", this);
11120         }else{
11121             this.totalLength = Math.max(t, this.data.length+r.length);
11122             this.add(r);
11123         }
11124         
11125         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11126                 
11127             var e = new Roo.data.Record({});
11128
11129             e.set(this.parent.displayField, this.parent.emptyTitle);
11130             e.set(this.parent.valueField, '');
11131
11132             this.insert(0, e);
11133         }
11134             
11135         this.fireEvent("load", this, r, options, o);
11136         if(options.callback){
11137             options.callback.call(options.scope || this, r, options, true);
11138         }
11139     },
11140
11141
11142     /**
11143      * Loads data from a passed data block. A Reader which understands the format of the data
11144      * must have been configured in the constructor.
11145      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11146      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11147      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11148      */
11149     loadData : function(o, append){
11150         var r = this.reader.readRecords(o);
11151         this.loadRecords(r, {add: append}, true);
11152     },
11153
11154     /**
11155      * Gets the number of cached records.
11156      * <p>
11157      * <em>If using paging, this may not be the total size of the dataset. If the data object
11158      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11159      * the data set size</em>
11160      */
11161     getCount : function(){
11162         return this.data.length || 0;
11163     },
11164
11165     /**
11166      * Gets the total number of records in the dataset as returned by the server.
11167      * <p>
11168      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11169      * the dataset size</em>
11170      */
11171     getTotalCount : function(){
11172         return this.totalLength || 0;
11173     },
11174
11175     /**
11176      * Returns the sort state of the Store as an object with two properties:
11177      * <pre><code>
11178  field {String} The name of the field by which the Records are sorted
11179  direction {String} The sort order, "ASC" or "DESC"
11180      * </code></pre>
11181      */
11182     getSortState : function(){
11183         return this.sortInfo;
11184     },
11185
11186     // private
11187     applySort : function(){
11188         if(this.sortInfo && !this.remoteSort){
11189             var s = this.sortInfo, f = s.field;
11190             var st = this.fields.get(f).sortType;
11191             var fn = function(r1, r2){
11192                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11193                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11194             };
11195             this.data.sort(s.direction, fn);
11196             if(this.snapshot && this.snapshot != this.data){
11197                 this.snapshot.sort(s.direction, fn);
11198             }
11199         }
11200     },
11201
11202     /**
11203      * Sets the default sort column and order to be used by the next load operation.
11204      * @param {String} fieldName The name of the field to sort by.
11205      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11206      */
11207     setDefaultSort : function(field, dir){
11208         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11209     },
11210
11211     /**
11212      * Sort the Records.
11213      * If remote sorting is used, the sort is performed on the server, and the cache is
11214      * reloaded. If local sorting is used, the cache is sorted internally.
11215      * @param {String} fieldName The name of the field to sort by.
11216      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11217      */
11218     sort : function(fieldName, dir){
11219         var f = this.fields.get(fieldName);
11220         if(!dir){
11221             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11222             
11223             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11224                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11225             }else{
11226                 dir = f.sortDir;
11227             }
11228         }
11229         this.sortToggle[f.name] = dir;
11230         this.sortInfo = {field: f.name, direction: dir};
11231         if(!this.remoteSort){
11232             this.applySort();
11233             this.fireEvent("datachanged", this);
11234         }else{
11235             this.load(this.lastOptions);
11236         }
11237     },
11238
11239     /**
11240      * Calls the specified function for each of the Records in the cache.
11241      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11242      * Returning <em>false</em> aborts and exits the iteration.
11243      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11244      */
11245     each : function(fn, scope){
11246         this.data.each(fn, scope);
11247     },
11248
11249     /**
11250      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11251      * (e.g., during paging).
11252      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11253      */
11254     getModifiedRecords : function(){
11255         return this.modified;
11256     },
11257
11258     // private
11259     createFilterFn : function(property, value, anyMatch){
11260         if(!value.exec){ // not a regex
11261             value = String(value);
11262             if(value.length == 0){
11263                 return false;
11264             }
11265             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11266         }
11267         return function(r){
11268             return value.test(r.data[property]);
11269         };
11270     },
11271
11272     /**
11273      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11274      * @param {String} property A field on your records
11275      * @param {Number} start The record index to start at (defaults to 0)
11276      * @param {Number} end The last record index to include (defaults to length - 1)
11277      * @return {Number} The sum
11278      */
11279     sum : function(property, start, end){
11280         var rs = this.data.items, v = 0;
11281         start = start || 0;
11282         end = (end || end === 0) ? end : rs.length-1;
11283
11284         for(var i = start; i <= end; i++){
11285             v += (rs[i].data[property] || 0);
11286         }
11287         return v;
11288     },
11289
11290     /**
11291      * Filter the records by a specified property.
11292      * @param {String} field A field on your records
11293      * @param {String/RegExp} value Either a string that the field
11294      * should start with or a RegExp to test against the field
11295      * @param {Boolean} anyMatch True to match any part not just the beginning
11296      */
11297     filter : function(property, value, anyMatch){
11298         var fn = this.createFilterFn(property, value, anyMatch);
11299         return fn ? this.filterBy(fn) : this.clearFilter();
11300     },
11301
11302     /**
11303      * Filter by a function. The specified function will be called with each
11304      * record in this data source. If the function returns true the record is included,
11305      * otherwise it is filtered.
11306      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11307      * @param {Object} scope (optional) The scope of the function (defaults to this)
11308      */
11309     filterBy : function(fn, scope){
11310         this.snapshot = this.snapshot || this.data;
11311         this.data = this.queryBy(fn, scope||this);
11312         this.fireEvent("datachanged", this);
11313     },
11314
11315     /**
11316      * Query the records by a specified property.
11317      * @param {String} field A field on your records
11318      * @param {String/RegExp} value Either a string that the field
11319      * should start with or a RegExp to test against the field
11320      * @param {Boolean} anyMatch True to match any part not just the beginning
11321      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11322      */
11323     query : function(property, value, anyMatch){
11324         var fn = this.createFilterFn(property, value, anyMatch);
11325         return fn ? this.queryBy(fn) : this.data.clone();
11326     },
11327
11328     /**
11329      * Query by a function. The specified function will be called with each
11330      * record in this data source. If the function returns true the record is included
11331      * in the results.
11332      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11333      * @param {Object} scope (optional) The scope of the function (defaults to this)
11334       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11335      **/
11336     queryBy : function(fn, scope){
11337         var data = this.snapshot || this.data;
11338         return data.filterBy(fn, scope||this);
11339     },
11340
11341     /**
11342      * Collects unique values for a particular dataIndex from this store.
11343      * @param {String} dataIndex The property to collect
11344      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11345      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11346      * @return {Array} An array of the unique values
11347      **/
11348     collect : function(dataIndex, allowNull, bypassFilter){
11349         var d = (bypassFilter === true && this.snapshot) ?
11350                 this.snapshot.items : this.data.items;
11351         var v, sv, r = [], l = {};
11352         for(var i = 0, len = d.length; i < len; i++){
11353             v = d[i].data[dataIndex];
11354             sv = String(v);
11355             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11356                 l[sv] = true;
11357                 r[r.length] = v;
11358             }
11359         }
11360         return r;
11361     },
11362
11363     /**
11364      * Revert to a view of the Record cache with no filtering applied.
11365      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11366      */
11367     clearFilter : function(suppressEvent){
11368         if(this.snapshot && this.snapshot != this.data){
11369             this.data = this.snapshot;
11370             delete this.snapshot;
11371             if(suppressEvent !== true){
11372                 this.fireEvent("datachanged", this);
11373             }
11374         }
11375     },
11376
11377     // private
11378     afterEdit : function(record){
11379         if(this.modified.indexOf(record) == -1){
11380             this.modified.push(record);
11381         }
11382         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11383     },
11384     
11385     // private
11386     afterReject : function(record){
11387         this.modified.remove(record);
11388         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11389     },
11390
11391     // private
11392     afterCommit : function(record){
11393         this.modified.remove(record);
11394         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11395     },
11396
11397     /**
11398      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11399      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11400      */
11401     commitChanges : function(){
11402         var m = this.modified.slice(0);
11403         this.modified = [];
11404         for(var i = 0, len = m.length; i < len; i++){
11405             m[i].commit();
11406         }
11407     },
11408
11409     /**
11410      * Cancel outstanding changes on all changed records.
11411      */
11412     rejectChanges : function(){
11413         var m = this.modified.slice(0);
11414         this.modified = [];
11415         for(var i = 0, len = m.length; i < len; i++){
11416             m[i].reject();
11417         }
11418     },
11419
11420     onMetaChange : function(meta, rtype, o){
11421         this.recordType = rtype;
11422         this.fields = rtype.prototype.fields;
11423         delete this.snapshot;
11424         this.sortInfo = meta.sortInfo || this.sortInfo;
11425         this.modified = [];
11426         this.fireEvent('metachange', this, this.reader.meta);
11427     },
11428     
11429     moveIndex : function(data, type)
11430     {
11431         var index = this.indexOf(data);
11432         
11433         var newIndex = index + type;
11434         
11435         this.remove(data);
11436         
11437         this.insert(newIndex, data);
11438         
11439     }
11440 });/*
11441  * Based on:
11442  * Ext JS Library 1.1.1
11443  * Copyright(c) 2006-2007, Ext JS, LLC.
11444  *
11445  * Originally Released Under LGPL - original licence link has changed is not relivant.
11446  *
11447  * Fork - LGPL
11448  * <script type="text/javascript">
11449  */
11450
11451 /**
11452  * @class Roo.data.SimpleStore
11453  * @extends Roo.data.Store
11454  * Small helper class to make creating Stores from Array data easier.
11455  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11456  * @cfg {Array} fields An array of field definition objects, or field name strings.
11457  * @cfg {Array} data The multi-dimensional array of data
11458  * @constructor
11459  * @param {Object} config
11460  */
11461 Roo.data.SimpleStore = function(config){
11462     Roo.data.SimpleStore.superclass.constructor.call(this, {
11463         isLocal : true,
11464         reader: new Roo.data.ArrayReader({
11465                 id: config.id
11466             },
11467             Roo.data.Record.create(config.fields)
11468         ),
11469         proxy : new Roo.data.MemoryProxy(config.data)
11470     });
11471     this.load();
11472 };
11473 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11474  * Based on:
11475  * Ext JS Library 1.1.1
11476  * Copyright(c) 2006-2007, Ext JS, LLC.
11477  *
11478  * Originally Released Under LGPL - original licence link has changed is not relivant.
11479  *
11480  * Fork - LGPL
11481  * <script type="text/javascript">
11482  */
11483
11484 /**
11485 /**
11486  * @extends Roo.data.Store
11487  * @class Roo.data.JsonStore
11488  * Small helper class to make creating Stores for JSON data easier. <br/>
11489 <pre><code>
11490 var store = new Roo.data.JsonStore({
11491     url: 'get-images.php',
11492     root: 'images',
11493     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11494 });
11495 </code></pre>
11496  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11497  * JsonReader and HttpProxy (unless inline data is provided).</b>
11498  * @cfg {Array} fields An array of field definition objects, or field name strings.
11499  * @constructor
11500  * @param {Object} config
11501  */
11502 Roo.data.JsonStore = function(c){
11503     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11504         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11505         reader: new Roo.data.JsonReader(c, c.fields)
11506     }));
11507 };
11508 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11509  * Based on:
11510  * Ext JS Library 1.1.1
11511  * Copyright(c) 2006-2007, Ext JS, LLC.
11512  *
11513  * Originally Released Under LGPL - original licence link has changed is not relivant.
11514  *
11515  * Fork - LGPL
11516  * <script type="text/javascript">
11517  */
11518
11519  
11520 Roo.data.Field = function(config){
11521     if(typeof config == "string"){
11522         config = {name: config};
11523     }
11524     Roo.apply(this, config);
11525     
11526     if(!this.type){
11527         this.type = "auto";
11528     }
11529     
11530     var st = Roo.data.SortTypes;
11531     // named sortTypes are supported, here we look them up
11532     if(typeof this.sortType == "string"){
11533         this.sortType = st[this.sortType];
11534     }
11535     
11536     // set default sortType for strings and dates
11537     if(!this.sortType){
11538         switch(this.type){
11539             case "string":
11540                 this.sortType = st.asUCString;
11541                 break;
11542             case "date":
11543                 this.sortType = st.asDate;
11544                 break;
11545             default:
11546                 this.sortType = st.none;
11547         }
11548     }
11549
11550     // define once
11551     var stripRe = /[\$,%]/g;
11552
11553     // prebuilt conversion function for this field, instead of
11554     // switching every time we're reading a value
11555     if(!this.convert){
11556         var cv, dateFormat = this.dateFormat;
11557         switch(this.type){
11558             case "":
11559             case "auto":
11560             case undefined:
11561                 cv = function(v){ return v; };
11562                 break;
11563             case "string":
11564                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11565                 break;
11566             case "int":
11567                 cv = function(v){
11568                     return v !== undefined && v !== null && v !== '' ?
11569                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11570                     };
11571                 break;
11572             case "float":
11573                 cv = function(v){
11574                     return v !== undefined && v !== null && v !== '' ?
11575                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11576                     };
11577                 break;
11578             case "bool":
11579             case "boolean":
11580                 cv = function(v){ return v === true || v === "true" || v == 1; };
11581                 break;
11582             case "date":
11583                 cv = function(v){
11584                     if(!v){
11585                         return '';
11586                     }
11587                     if(v instanceof Date){
11588                         return v;
11589                     }
11590                     if(dateFormat){
11591                         if(dateFormat == "timestamp"){
11592                             return new Date(v*1000);
11593                         }
11594                         return Date.parseDate(v, dateFormat);
11595                     }
11596                     var parsed = Date.parse(v);
11597                     return parsed ? new Date(parsed) : null;
11598                 };
11599              break;
11600             
11601         }
11602         this.convert = cv;
11603     }
11604 };
11605
11606 Roo.data.Field.prototype = {
11607     dateFormat: null,
11608     defaultValue: "",
11609     mapping: null,
11610     sortType : null,
11611     sortDir : "ASC"
11612 };/*
11613  * Based on:
11614  * Ext JS Library 1.1.1
11615  * Copyright(c) 2006-2007, Ext JS, LLC.
11616  *
11617  * Originally Released Under LGPL - original licence link has changed is not relivant.
11618  *
11619  * Fork - LGPL
11620  * <script type="text/javascript">
11621  */
11622  
11623 // Base class for reading structured data from a data source.  This class is intended to be
11624 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11625
11626 /**
11627  * @class Roo.data.DataReader
11628  * Base class for reading structured data from a data source.  This class is intended to be
11629  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11630  */
11631
11632 Roo.data.DataReader = function(meta, recordType){
11633     
11634     this.meta = meta;
11635     
11636     this.recordType = recordType instanceof Array ? 
11637         Roo.data.Record.create(recordType) : recordType;
11638 };
11639
11640 Roo.data.DataReader.prototype = {
11641      /**
11642      * Create an empty record
11643      * @param {Object} data (optional) - overlay some values
11644      * @return {Roo.data.Record} record created.
11645      */
11646     newRow :  function(d) {
11647         var da =  {};
11648         this.recordType.prototype.fields.each(function(c) {
11649             switch( c.type) {
11650                 case 'int' : da[c.name] = 0; break;
11651                 case 'date' : da[c.name] = new Date(); break;
11652                 case 'float' : da[c.name] = 0.0; break;
11653                 case 'boolean' : da[c.name] = false; break;
11654                 default : da[c.name] = ""; break;
11655             }
11656             
11657         });
11658         return new this.recordType(Roo.apply(da, d));
11659     }
11660     
11661 };/*
11662  * Based on:
11663  * Ext JS Library 1.1.1
11664  * Copyright(c) 2006-2007, Ext JS, LLC.
11665  *
11666  * Originally Released Under LGPL - original licence link has changed is not relivant.
11667  *
11668  * Fork - LGPL
11669  * <script type="text/javascript">
11670  */
11671
11672 /**
11673  * @class Roo.data.DataProxy
11674  * @extends Roo.data.Observable
11675  * This class is an abstract base class for implementations which provide retrieval of
11676  * unformatted data objects.<br>
11677  * <p>
11678  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11679  * (of the appropriate type which knows how to parse the data object) to provide a block of
11680  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11681  * <p>
11682  * Custom implementations must implement the load method as described in
11683  * {@link Roo.data.HttpProxy#load}.
11684  */
11685 Roo.data.DataProxy = function(){
11686     this.addEvents({
11687         /**
11688          * @event beforeload
11689          * Fires before a network request is made to retrieve a data object.
11690          * @param {Object} This DataProxy object.
11691          * @param {Object} params The params parameter to the load function.
11692          */
11693         beforeload : true,
11694         /**
11695          * @event load
11696          * Fires before the load method's callback is called.
11697          * @param {Object} This DataProxy object.
11698          * @param {Object} o The data object.
11699          * @param {Object} arg The callback argument object passed to the load function.
11700          */
11701         load : true,
11702         /**
11703          * @event loadexception
11704          * Fires if an Exception occurs during data retrieval.
11705          * @param {Object} This DataProxy object.
11706          * @param {Object} o The data object.
11707          * @param {Object} arg The callback argument object passed to the load function.
11708          * @param {Object} e The Exception.
11709          */
11710         loadexception : true
11711     });
11712     Roo.data.DataProxy.superclass.constructor.call(this);
11713 };
11714
11715 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11716
11717     /**
11718      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11719      */
11720 /*
11721  * Based on:
11722  * Ext JS Library 1.1.1
11723  * Copyright(c) 2006-2007, Ext JS, LLC.
11724  *
11725  * Originally Released Under LGPL - original licence link has changed is not relivant.
11726  *
11727  * Fork - LGPL
11728  * <script type="text/javascript">
11729  */
11730 /**
11731  * @class Roo.data.MemoryProxy
11732  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11733  * to the Reader when its load method is called.
11734  * @constructor
11735  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11736  */
11737 Roo.data.MemoryProxy = function(data){
11738     if (data.data) {
11739         data = data.data;
11740     }
11741     Roo.data.MemoryProxy.superclass.constructor.call(this);
11742     this.data = data;
11743 };
11744
11745 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11746     
11747     /**
11748      * Load data from the requested source (in this case an in-memory
11749      * data object passed to the constructor), read the data object into
11750      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11751      * process that block using the passed callback.
11752      * @param {Object} params This parameter is not used by the MemoryProxy class.
11753      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11754      * object into a block of Roo.data.Records.
11755      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11756      * The function must be passed <ul>
11757      * <li>The Record block object</li>
11758      * <li>The "arg" argument from the load function</li>
11759      * <li>A boolean success indicator</li>
11760      * </ul>
11761      * @param {Object} scope The scope in which to call the callback
11762      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11763      */
11764     load : function(params, reader, callback, scope, arg){
11765         params = params || {};
11766         var result;
11767         try {
11768             result = reader.readRecords(this.data);
11769         }catch(e){
11770             this.fireEvent("loadexception", this, arg, null, e);
11771             callback.call(scope, null, arg, false);
11772             return;
11773         }
11774         callback.call(scope, result, arg, true);
11775     },
11776     
11777     // private
11778     update : function(params, records){
11779         
11780     }
11781 });/*
11782  * Based on:
11783  * Ext JS Library 1.1.1
11784  * Copyright(c) 2006-2007, Ext JS, LLC.
11785  *
11786  * Originally Released Under LGPL - original licence link has changed is not relivant.
11787  *
11788  * Fork - LGPL
11789  * <script type="text/javascript">
11790  */
11791 /**
11792  * @class Roo.data.HttpProxy
11793  * @extends Roo.data.DataProxy
11794  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11795  * configured to reference a certain URL.<br><br>
11796  * <p>
11797  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11798  * from which the running page was served.<br><br>
11799  * <p>
11800  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11801  * <p>
11802  * Be aware that to enable the browser to parse an XML document, the server must set
11803  * the Content-Type header in the HTTP response to "text/xml".
11804  * @constructor
11805  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11806  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11807  * will be used to make the request.
11808  */
11809 Roo.data.HttpProxy = function(conn){
11810     Roo.data.HttpProxy.superclass.constructor.call(this);
11811     // is conn a conn config or a real conn?
11812     this.conn = conn;
11813     this.useAjax = !conn || !conn.events;
11814   
11815 };
11816
11817 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11818     // thse are take from connection...
11819     
11820     /**
11821      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11822      */
11823     /**
11824      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11825      * extra parameters to each request made by this object. (defaults to undefined)
11826      */
11827     /**
11828      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11829      *  to each request made by this object. (defaults to undefined)
11830      */
11831     /**
11832      * @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)
11833      */
11834     /**
11835      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11836      */
11837      /**
11838      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11839      * @type Boolean
11840      */
11841   
11842
11843     /**
11844      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11845      * @type Boolean
11846      */
11847     /**
11848      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11849      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11850      * a finer-grained basis than the DataProxy events.
11851      */
11852     getConnection : function(){
11853         return this.useAjax ? Roo.Ajax : this.conn;
11854     },
11855
11856     /**
11857      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11858      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11859      * process that block using the passed callback.
11860      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11861      * for the request to the remote server.
11862      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11863      * object into a block of Roo.data.Records.
11864      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11865      * The function must be passed <ul>
11866      * <li>The Record block object</li>
11867      * <li>The "arg" argument from the load function</li>
11868      * <li>A boolean success indicator</li>
11869      * </ul>
11870      * @param {Object} scope The scope in which to call the callback
11871      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11872      */
11873     load : function(params, reader, callback, scope, arg){
11874         if(this.fireEvent("beforeload", this, params) !== false){
11875             var  o = {
11876                 params : params || {},
11877                 request: {
11878                     callback : callback,
11879                     scope : scope,
11880                     arg : arg
11881                 },
11882                 reader: reader,
11883                 callback : this.loadResponse,
11884                 scope: this
11885             };
11886             if(this.useAjax){
11887                 Roo.applyIf(o, this.conn);
11888                 if(this.activeRequest){
11889                     Roo.Ajax.abort(this.activeRequest);
11890                 }
11891                 this.activeRequest = Roo.Ajax.request(o);
11892             }else{
11893                 this.conn.request(o);
11894             }
11895         }else{
11896             callback.call(scope||this, null, arg, false);
11897         }
11898     },
11899
11900     // private
11901     loadResponse : function(o, success, response){
11902         delete this.activeRequest;
11903         if(!success){
11904             this.fireEvent("loadexception", this, o, response);
11905             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11906             return;
11907         }
11908         var result;
11909         try {
11910             result = o.reader.read(response);
11911         }catch(e){
11912             this.fireEvent("loadexception", this, o, response, e);
11913             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11914             return;
11915         }
11916         
11917         this.fireEvent("load", this, o, o.request.arg);
11918         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11919     },
11920
11921     // private
11922     update : function(dataSet){
11923
11924     },
11925
11926     // private
11927     updateResponse : function(dataSet){
11928
11929     }
11930 });/*
11931  * Based on:
11932  * Ext JS Library 1.1.1
11933  * Copyright(c) 2006-2007, Ext JS, LLC.
11934  *
11935  * Originally Released Under LGPL - original licence link has changed is not relivant.
11936  *
11937  * Fork - LGPL
11938  * <script type="text/javascript">
11939  */
11940
11941 /**
11942  * @class Roo.data.ScriptTagProxy
11943  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11944  * other than the originating domain of the running page.<br><br>
11945  * <p>
11946  * <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
11947  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11948  * <p>
11949  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11950  * source code that is used as the source inside a &lt;script> tag.<br><br>
11951  * <p>
11952  * In order for the browser to process the returned data, the server must wrap the data object
11953  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11954  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11955  * depending on whether the callback name was passed:
11956  * <p>
11957  * <pre><code>
11958 boolean scriptTag = false;
11959 String cb = request.getParameter("callback");
11960 if (cb != null) {
11961     scriptTag = true;
11962     response.setContentType("text/javascript");
11963 } else {
11964     response.setContentType("application/x-json");
11965 }
11966 Writer out = response.getWriter();
11967 if (scriptTag) {
11968     out.write(cb + "(");
11969 }
11970 out.print(dataBlock.toJsonString());
11971 if (scriptTag) {
11972     out.write(");");
11973 }
11974 </pre></code>
11975  *
11976  * @constructor
11977  * @param {Object} config A configuration object.
11978  */
11979 Roo.data.ScriptTagProxy = function(config){
11980     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11981     Roo.apply(this, config);
11982     this.head = document.getElementsByTagName("head")[0];
11983 };
11984
11985 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11986
11987 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11988     /**
11989      * @cfg {String} url The URL from which to request the data object.
11990      */
11991     /**
11992      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11993      */
11994     timeout : 30000,
11995     /**
11996      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11997      * the server the name of the callback function set up by the load call to process the returned data object.
11998      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11999      * javascript output which calls this named function passing the data object as its only parameter.
12000      */
12001     callbackParam : "callback",
12002     /**
12003      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12004      * name to the request.
12005      */
12006     nocache : true,
12007
12008     /**
12009      * Load data from the configured URL, read the data object into
12010      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12011      * process that block using the passed callback.
12012      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12013      * for the request to the remote server.
12014      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12015      * object into a block of Roo.data.Records.
12016      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12017      * The function must be passed <ul>
12018      * <li>The Record block object</li>
12019      * <li>The "arg" argument from the load function</li>
12020      * <li>A boolean success indicator</li>
12021      * </ul>
12022      * @param {Object} scope The scope in which to call the callback
12023      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12024      */
12025     load : function(params, reader, callback, scope, arg){
12026         if(this.fireEvent("beforeload", this, params) !== false){
12027
12028             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12029
12030             var url = this.url;
12031             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12032             if(this.nocache){
12033                 url += "&_dc=" + (new Date().getTime());
12034             }
12035             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12036             var trans = {
12037                 id : transId,
12038                 cb : "stcCallback"+transId,
12039                 scriptId : "stcScript"+transId,
12040                 params : params,
12041                 arg : arg,
12042                 url : url,
12043                 callback : callback,
12044                 scope : scope,
12045                 reader : reader
12046             };
12047             var conn = this;
12048
12049             window[trans.cb] = function(o){
12050                 conn.handleResponse(o, trans);
12051             };
12052
12053             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12054
12055             if(this.autoAbort !== false){
12056                 this.abort();
12057             }
12058
12059             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12060
12061             var script = document.createElement("script");
12062             script.setAttribute("src", url);
12063             script.setAttribute("type", "text/javascript");
12064             script.setAttribute("id", trans.scriptId);
12065             this.head.appendChild(script);
12066
12067             this.trans = trans;
12068         }else{
12069             callback.call(scope||this, null, arg, false);
12070         }
12071     },
12072
12073     // private
12074     isLoading : function(){
12075         return this.trans ? true : false;
12076     },
12077
12078     /**
12079      * Abort the current server request.
12080      */
12081     abort : function(){
12082         if(this.isLoading()){
12083             this.destroyTrans(this.trans);
12084         }
12085     },
12086
12087     // private
12088     destroyTrans : function(trans, isLoaded){
12089         this.head.removeChild(document.getElementById(trans.scriptId));
12090         clearTimeout(trans.timeoutId);
12091         if(isLoaded){
12092             window[trans.cb] = undefined;
12093             try{
12094                 delete window[trans.cb];
12095             }catch(e){}
12096         }else{
12097             // if hasn't been loaded, wait for load to remove it to prevent script error
12098             window[trans.cb] = function(){
12099                 window[trans.cb] = undefined;
12100                 try{
12101                     delete window[trans.cb];
12102                 }catch(e){}
12103             };
12104         }
12105     },
12106
12107     // private
12108     handleResponse : function(o, trans){
12109         this.trans = false;
12110         this.destroyTrans(trans, true);
12111         var result;
12112         try {
12113             result = trans.reader.readRecords(o);
12114         }catch(e){
12115             this.fireEvent("loadexception", this, o, trans.arg, e);
12116             trans.callback.call(trans.scope||window, null, trans.arg, false);
12117             return;
12118         }
12119         this.fireEvent("load", this, o, trans.arg);
12120         trans.callback.call(trans.scope||window, result, trans.arg, true);
12121     },
12122
12123     // private
12124     handleFailure : function(trans){
12125         this.trans = false;
12126         this.destroyTrans(trans, false);
12127         this.fireEvent("loadexception", this, null, trans.arg);
12128         trans.callback.call(trans.scope||window, null, trans.arg, false);
12129     }
12130 });/*
12131  * Based on:
12132  * Ext JS Library 1.1.1
12133  * Copyright(c) 2006-2007, Ext JS, LLC.
12134  *
12135  * Originally Released Under LGPL - original licence link has changed is not relivant.
12136  *
12137  * Fork - LGPL
12138  * <script type="text/javascript">
12139  */
12140
12141 /**
12142  * @class Roo.data.JsonReader
12143  * @extends Roo.data.DataReader
12144  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12145  * based on mappings in a provided Roo.data.Record constructor.
12146  * 
12147  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12148  * in the reply previously. 
12149  * 
12150  * <p>
12151  * Example code:
12152  * <pre><code>
12153 var RecordDef = Roo.data.Record.create([
12154     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12155     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12156 ]);
12157 var myReader = new Roo.data.JsonReader({
12158     totalProperty: "results",    // The property which contains the total dataset size (optional)
12159     root: "rows",                // The property which contains an Array of row objects
12160     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12161 }, RecordDef);
12162 </code></pre>
12163  * <p>
12164  * This would consume a JSON file like this:
12165  * <pre><code>
12166 { 'results': 2, 'rows': [
12167     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12168     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12169 }
12170 </code></pre>
12171  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12172  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12173  * paged from the remote server.
12174  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12175  * @cfg {String} root name of the property which contains the Array of row objects.
12176  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12177  * @cfg {Array} fields Array of field definition objects
12178  * @constructor
12179  * Create a new JsonReader
12180  * @param {Object} meta Metadata configuration options
12181  * @param {Object} recordType Either an Array of field definition objects,
12182  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12183  */
12184 Roo.data.JsonReader = function(meta, recordType){
12185     
12186     meta = meta || {};
12187     // set some defaults:
12188     Roo.applyIf(meta, {
12189         totalProperty: 'total',
12190         successProperty : 'success',
12191         root : 'data',
12192         id : 'id'
12193     });
12194     
12195     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12196 };
12197 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12198     
12199     /**
12200      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12201      * Used by Store query builder to append _requestMeta to params.
12202      * 
12203      */
12204     metaFromRemote : false,
12205     /**
12206      * This method is only used by a DataProxy which has retrieved data from a remote server.
12207      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12208      * @return {Object} data A data block which is used by an Roo.data.Store object as
12209      * a cache of Roo.data.Records.
12210      */
12211     read : function(response){
12212         var json = response.responseText;
12213        
12214         var o = /* eval:var:o */ eval("("+json+")");
12215         if(!o) {
12216             throw {message: "JsonReader.read: Json object not found"};
12217         }
12218         
12219         if(o.metaData){
12220             
12221             delete this.ef;
12222             this.metaFromRemote = true;
12223             this.meta = o.metaData;
12224             this.recordType = Roo.data.Record.create(o.metaData.fields);
12225             this.onMetaChange(this.meta, this.recordType, o);
12226         }
12227         return this.readRecords(o);
12228     },
12229
12230     // private function a store will implement
12231     onMetaChange : function(meta, recordType, o){
12232
12233     },
12234
12235     /**
12236          * @ignore
12237          */
12238     simpleAccess: function(obj, subsc) {
12239         return obj[subsc];
12240     },
12241
12242         /**
12243          * @ignore
12244          */
12245     getJsonAccessor: function(){
12246         var re = /[\[\.]/;
12247         return function(expr) {
12248             try {
12249                 return(re.test(expr))
12250                     ? new Function("obj", "return obj." + expr)
12251                     : function(obj){
12252                         return obj[expr];
12253                     };
12254             } catch(e){}
12255             return Roo.emptyFn;
12256         };
12257     }(),
12258
12259     /**
12260      * Create a data block containing Roo.data.Records from an XML document.
12261      * @param {Object} o An object which contains an Array of row objects in the property specified
12262      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12263      * which contains the total size of the dataset.
12264      * @return {Object} data A data block which is used by an Roo.data.Store object as
12265      * a cache of Roo.data.Records.
12266      */
12267     readRecords : function(o){
12268         /**
12269          * After any data loads, the raw JSON data is available for further custom processing.
12270          * @type Object
12271          */
12272         this.o = o;
12273         var s = this.meta, Record = this.recordType,
12274             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12275
12276 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12277         if (!this.ef) {
12278             if(s.totalProperty) {
12279                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12280                 }
12281                 if(s.successProperty) {
12282                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12283                 }
12284                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12285                 if (s.id) {
12286                         var g = this.getJsonAccessor(s.id);
12287                         this.getId = function(rec) {
12288                                 var r = g(rec);  
12289                                 return (r === undefined || r === "") ? null : r;
12290                         };
12291                 } else {
12292                         this.getId = function(){return null;};
12293                 }
12294             this.ef = [];
12295             for(var jj = 0; jj < fl; jj++){
12296                 f = fi[jj];
12297                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12298                 this.ef[jj] = this.getJsonAccessor(map);
12299             }
12300         }
12301
12302         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12303         if(s.totalProperty){
12304             var vt = parseInt(this.getTotal(o), 10);
12305             if(!isNaN(vt)){
12306                 totalRecords = vt;
12307             }
12308         }
12309         if(s.successProperty){
12310             var vs = this.getSuccess(o);
12311             if(vs === false || vs === 'false'){
12312                 success = false;
12313             }
12314         }
12315         var records = [];
12316         for(var i = 0; i < c; i++){
12317                 var n = root[i];
12318             var values = {};
12319             var id = this.getId(n);
12320             for(var j = 0; j < fl; j++){
12321                 f = fi[j];
12322             var v = this.ef[j](n);
12323             if (!f.convert) {
12324                 Roo.log('missing convert for ' + f.name);
12325                 Roo.log(f);
12326                 continue;
12327             }
12328             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12329             }
12330             var record = new Record(values, id);
12331             record.json = n;
12332             records[i] = record;
12333         }
12334         return {
12335             raw : o,
12336             success : success,
12337             records : records,
12338             totalRecords : totalRecords
12339         };
12340     }
12341 });/*
12342  * Based on:
12343  * Ext JS Library 1.1.1
12344  * Copyright(c) 2006-2007, Ext JS, LLC.
12345  *
12346  * Originally Released Under LGPL - original licence link has changed is not relivant.
12347  *
12348  * Fork - LGPL
12349  * <script type="text/javascript">
12350  */
12351
12352 /**
12353  * @class Roo.data.ArrayReader
12354  * @extends Roo.data.DataReader
12355  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12356  * Each element of that Array represents a row of data fields. The
12357  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12358  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12359  * <p>
12360  * Example code:.
12361  * <pre><code>
12362 var RecordDef = Roo.data.Record.create([
12363     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12364     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12365 ]);
12366 var myReader = new Roo.data.ArrayReader({
12367     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12368 }, RecordDef);
12369 </code></pre>
12370  * <p>
12371  * This would consume an Array like this:
12372  * <pre><code>
12373 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12374   </code></pre>
12375  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12376  * @constructor
12377  * Create a new JsonReader
12378  * @param {Object} meta Metadata configuration options.
12379  * @param {Object} recordType Either an Array of field definition objects
12380  * as specified to {@link Roo.data.Record#create},
12381  * or an {@link Roo.data.Record} object
12382  * created using {@link Roo.data.Record#create}.
12383  */
12384 Roo.data.ArrayReader = function(meta, recordType){
12385     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12386 };
12387
12388 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12389     /**
12390      * Create a data block containing Roo.data.Records from an XML document.
12391      * @param {Object} o An Array of row objects which represents the dataset.
12392      * @return {Object} data A data block which is used by an Roo.data.Store object as
12393      * a cache of Roo.data.Records.
12394      */
12395     readRecords : function(o){
12396         var sid = this.meta ? this.meta.id : null;
12397         var recordType = this.recordType, fields = recordType.prototype.fields;
12398         var records = [];
12399         var root = o;
12400             for(var i = 0; i < root.length; i++){
12401                     var n = root[i];
12402                 var values = {};
12403                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12405                 var f = fields.items[j];
12406                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12407                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12408                 v = f.convert(v);
12409                 values[f.name] = v;
12410             }
12411                 var record = new recordType(values, id);
12412                 record.json = n;
12413                 records[records.length] = record;
12414             }
12415             return {
12416                 records : records,
12417                 totalRecords : records.length
12418             };
12419     }
12420 });/*
12421  * - LGPL
12422  * * 
12423  */
12424
12425 /**
12426  * @class Roo.bootstrap.ComboBox
12427  * @extends Roo.bootstrap.TriggerField
12428  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12429  * @cfg {Boolean} append (true|false) default false
12430  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12431  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12432  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12433  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12434  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12435  * @cfg {Boolean} animate default true
12436  * @cfg {Boolean} emptyResultText only for touch device
12437  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12438  * @cfg {String} emptyTitle default ''
12439  * @constructor
12440  * Create a new ComboBox.
12441  * @param {Object} config Configuration options
12442  */
12443 Roo.bootstrap.ComboBox = function(config){
12444     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12445     this.addEvents({
12446         /**
12447          * @event expand
12448          * Fires when the dropdown list is expanded
12449         * @param {Roo.bootstrap.ComboBox} combo This combo box
12450         */
12451         'expand' : true,
12452         /**
12453          * @event collapse
12454          * Fires when the dropdown list is collapsed
12455         * @param {Roo.bootstrap.ComboBox} combo This combo box
12456         */
12457         'collapse' : true,
12458         /**
12459          * @event beforeselect
12460          * Fires before a list item is selected. Return false to cancel the selection.
12461         * @param {Roo.bootstrap.ComboBox} combo This combo box
12462         * @param {Roo.data.Record} record The data record returned from the underlying store
12463         * @param {Number} index The index of the selected item in the dropdown list
12464         */
12465         'beforeselect' : true,
12466         /**
12467          * @event select
12468          * Fires when a list item is selected
12469         * @param {Roo.bootstrap.ComboBox} combo This combo box
12470         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12471         * @param {Number} index The index of the selected item in the dropdown list
12472         */
12473         'select' : true,
12474         /**
12475          * @event beforequery
12476          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12477          * The event object passed has these properties:
12478         * @param {Roo.bootstrap.ComboBox} combo This combo box
12479         * @param {String} query The query
12480         * @param {Boolean} forceAll true to force "all" query
12481         * @param {Boolean} cancel true to cancel the query
12482         * @param {Object} e The query event object
12483         */
12484         'beforequery': true,
12485          /**
12486          * @event add
12487          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         */
12490         'add' : true,
12491         /**
12492          * @event edit
12493          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12496         */
12497         'edit' : true,
12498         /**
12499          * @event remove
12500          * Fires when the remove value from the combobox array
12501         * @param {Roo.bootstrap.ComboBox} combo This combo box
12502         */
12503         'remove' : true,
12504         /**
12505          * @event afterremove
12506          * Fires when the remove value from the combobox array
12507         * @param {Roo.bootstrap.ComboBox} combo This combo box
12508         */
12509         'afterremove' : true,
12510         /**
12511          * @event specialfilter
12512          * Fires when specialfilter
12513             * @param {Roo.bootstrap.ComboBox} combo This combo box
12514             */
12515         'specialfilter' : true,
12516         /**
12517          * @event tick
12518          * Fires when tick the element
12519             * @param {Roo.bootstrap.ComboBox} combo This combo box
12520             */
12521         'tick' : true,
12522         /**
12523          * @event touchviewdisplay
12524          * Fires when touch view require special display (default is using displayField)
12525             * @param {Roo.bootstrap.ComboBox} combo This combo box
12526             * @param {Object} cfg set html .
12527             */
12528         'touchviewdisplay' : true
12529         
12530     });
12531     
12532     this.item = [];
12533     this.tickItems = [];
12534     
12535     this.selectedIndex = -1;
12536     if(this.mode == 'local'){
12537         if(config.queryDelay === undefined){
12538             this.queryDelay = 10;
12539         }
12540         if(config.minChars === undefined){
12541             this.minChars = 0;
12542         }
12543     }
12544 };
12545
12546 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12547      
12548     /**
12549      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12550      * rendering into an Roo.Editor, defaults to false)
12551      */
12552     /**
12553      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12554      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12555      */
12556     /**
12557      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12558      */
12559     /**
12560      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12561      * the dropdown list (defaults to undefined, with no header element)
12562      */
12563
12564      /**
12565      * @cfg {String/Roo.Template} tpl The template to use to render the output
12566      */
12567      
12568      /**
12569      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12570      */
12571     listWidth: undefined,
12572     /**
12573      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12574      * mode = 'remote' or 'text' if mode = 'local')
12575      */
12576     displayField: undefined,
12577     
12578     /**
12579      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12580      * mode = 'remote' or 'value' if mode = 'local'). 
12581      * Note: use of a valueField requires the user make a selection
12582      * in order for a value to be mapped.
12583      */
12584     valueField: undefined,
12585     /**
12586      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12587      */
12588     modalTitle : '',
12589     
12590     /**
12591      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12592      * field's data value (defaults to the underlying DOM element's name)
12593      */
12594     hiddenName: undefined,
12595     /**
12596      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12597      */
12598     listClass: '',
12599     /**
12600      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12601      */
12602     selectedClass: 'active',
12603     
12604     /**
12605      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12606      */
12607     shadow:'sides',
12608     /**
12609      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12610      * anchor positions (defaults to 'tl-bl')
12611      */
12612     listAlign: 'tl-bl?',
12613     /**
12614      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12615      */
12616     maxHeight: 300,
12617     /**
12618      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12619      * query specified by the allQuery config option (defaults to 'query')
12620      */
12621     triggerAction: 'query',
12622     /**
12623      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12624      * (defaults to 4, does not apply if editable = false)
12625      */
12626     minChars : 4,
12627     /**
12628      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12629      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12630      */
12631     typeAhead: false,
12632     /**
12633      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12634      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12635      */
12636     queryDelay: 500,
12637     /**
12638      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12639      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12640      */
12641     pageSize: 0,
12642     /**
12643      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12644      * when editable = true (defaults to false)
12645      */
12646     selectOnFocus:false,
12647     /**
12648      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12649      */
12650     queryParam: 'query',
12651     /**
12652      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12653      * when mode = 'remote' (defaults to 'Loading...')
12654      */
12655     loadingText: 'Loading...',
12656     /**
12657      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12658      */
12659     resizable: false,
12660     /**
12661      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12662      */
12663     handleHeight : 8,
12664     /**
12665      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12666      * traditional select (defaults to true)
12667      */
12668     editable: true,
12669     /**
12670      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12671      */
12672     allQuery: '',
12673     /**
12674      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12675      */
12676     mode: 'remote',
12677     /**
12678      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12679      * listWidth has a higher value)
12680      */
12681     minListWidth : 70,
12682     /**
12683      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12684      * allow the user to set arbitrary text into the field (defaults to false)
12685      */
12686     forceSelection:false,
12687     /**
12688      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12689      * if typeAhead = true (defaults to 250)
12690      */
12691     typeAheadDelay : 250,
12692     /**
12693      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12694      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12695      */
12696     valueNotFoundText : undefined,
12697     /**
12698      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12699      */
12700     blockFocus : false,
12701     
12702     /**
12703      * @cfg {Boolean} disableClear Disable showing of clear button.
12704      */
12705     disableClear : false,
12706     /**
12707      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12708      */
12709     alwaysQuery : false,
12710     
12711     /**
12712      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12713      */
12714     multiple : false,
12715     
12716     /**
12717      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12718      */
12719     invalidClass : "has-warning",
12720     
12721     /**
12722      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12723      */
12724     validClass : "has-success",
12725     
12726     /**
12727      * @cfg {Boolean} specialFilter (true|false) special filter default false
12728      */
12729     specialFilter : false,
12730     
12731     /**
12732      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12733      */
12734     mobileTouchView : true,
12735     
12736     /**
12737      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12738      */
12739     useNativeIOS : false,
12740     
12741     ios_options : false,
12742     
12743     //private
12744     addicon : false,
12745     editicon: false,
12746     
12747     page: 0,
12748     hasQuery: false,
12749     append: false,
12750     loadNext: false,
12751     autoFocus : true,
12752     tickable : false,
12753     btnPosition : 'right',
12754     triggerList : true,
12755     showToggleBtn : true,
12756     animate : true,
12757     emptyResultText: 'Empty',
12758     triggerText : 'Select',
12759     emptyTitle : '',
12760     
12761     // element that contains real text value.. (when hidden is used..)
12762     
12763     getAutoCreate : function()
12764     {   
12765         var cfg = false;
12766         //render
12767         /*
12768          * Render classic select for iso
12769          */
12770         
12771         if(Roo.isIOS && this.useNativeIOS){
12772             cfg = this.getAutoCreateNativeIOS();
12773             return cfg;
12774         }
12775         
12776         /*
12777          * Touch Devices
12778          */
12779         
12780         if(Roo.isTouch && this.mobileTouchView){
12781             cfg = this.getAutoCreateTouchView();
12782             return cfg;;
12783         }
12784         
12785         /*
12786          *  Normal ComboBox
12787          */
12788         if(!this.tickable){
12789             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12790             return cfg;
12791         }
12792         
12793         /*
12794          *  ComboBox with tickable selections
12795          */
12796              
12797         var align = this.labelAlign || this.parentLabelAlign();
12798         
12799         cfg = {
12800             cls : 'form-group roo-combobox-tickable' //input-group
12801         };
12802         
12803         var btn_text_select = '';
12804         var btn_text_done = '';
12805         var btn_text_cancel = '';
12806         
12807         if (this.btn_text_show) {
12808             btn_text_select = 'Select';
12809             btn_text_done = 'Done';
12810             btn_text_cancel = 'Cancel'; 
12811         }
12812         
12813         var buttons = {
12814             tag : 'div',
12815             cls : 'tickable-buttons',
12816             cn : [
12817                 {
12818                     tag : 'button',
12819                     type : 'button',
12820                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12821                     //html : this.triggerText
12822                     html: btn_text_select
12823                 },
12824                 {
12825                     tag : 'button',
12826                     type : 'button',
12827                     name : 'ok',
12828                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12829                     //html : 'Done'
12830                     html: btn_text_done
12831                 },
12832                 {
12833                     tag : 'button',
12834                     type : 'button',
12835                     name : 'cancel',
12836                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12837                     //html : 'Cancel'
12838                     html: btn_text_cancel
12839                 }
12840             ]
12841         };
12842         
12843         if(this.editable){
12844             buttons.cn.unshift({
12845                 tag: 'input',
12846                 cls: 'roo-select2-search-field-input'
12847             });
12848         }
12849         
12850         var _this = this;
12851         
12852         Roo.each(buttons.cn, function(c){
12853             if (_this.size) {
12854                 c.cls += ' btn-' + _this.size;
12855             }
12856
12857             if (_this.disabled) {
12858                 c.disabled = true;
12859             }
12860         });
12861         
12862         var box = {
12863             tag: 'div',
12864             cn: [
12865                 {
12866                     tag: 'input',
12867                     type : 'hidden',
12868                     cls: 'form-hidden-field'
12869                 },
12870                 {
12871                     tag: 'ul',
12872                     cls: 'roo-select2-choices',
12873                     cn:[
12874                         {
12875                             tag: 'li',
12876                             cls: 'roo-select2-search-field',
12877                             cn: [
12878                                 buttons
12879                             ]
12880                         }
12881                     ]
12882                 }
12883             ]
12884         };
12885         
12886         var combobox = {
12887             cls: 'roo-select2-container input-group roo-select2-container-multi',
12888             cn: [
12889                 box
12890 //                {
12891 //                    tag: 'ul',
12892 //                    cls: 'typeahead typeahead-long dropdown-menu',
12893 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12894 //                }
12895             ]
12896         };
12897         
12898         if(this.hasFeedback && !this.allowBlank){
12899             
12900             var feedback = {
12901                 tag: 'span',
12902                 cls: 'glyphicon form-control-feedback'
12903             };
12904
12905             combobox.cn.push(feedback);
12906         }
12907         
12908         
12909         if (align ==='left' && this.fieldLabel.length) {
12910             
12911             cfg.cls += ' roo-form-group-label-left';
12912             
12913             cfg.cn = [
12914                 {
12915                     tag : 'i',
12916                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12917                     tooltip : 'This field is required'
12918                 },
12919                 {
12920                     tag: 'label',
12921                     'for' :  id,
12922                     cls : 'control-label',
12923                     html : this.fieldLabel
12924
12925                 },
12926                 {
12927                     cls : "", 
12928                     cn: [
12929                         combobox
12930                     ]
12931                 }
12932
12933             ];
12934             
12935             var labelCfg = cfg.cn[1];
12936             var contentCfg = cfg.cn[2];
12937             
12938
12939             if(this.indicatorpos == 'right'){
12940                 
12941                 cfg.cn = [
12942                     {
12943                         tag: 'label',
12944                         'for' :  id,
12945                         cls : 'control-label',
12946                         cn : [
12947                             {
12948                                 tag : 'span',
12949                                 html : this.fieldLabel
12950                             },
12951                             {
12952                                 tag : 'i',
12953                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12954                                 tooltip : 'This field is required'
12955                             }
12956                         ]
12957                     },
12958                     {
12959                         cls : "",
12960                         cn: [
12961                             combobox
12962                         ]
12963                     }
12964
12965                 ];
12966                 
12967                 
12968                 
12969                 labelCfg = cfg.cn[0];
12970                 contentCfg = cfg.cn[1];
12971             
12972             }
12973             
12974             if(this.labelWidth > 12){
12975                 labelCfg.style = "width: " + this.labelWidth + 'px';
12976             }
12977             
12978             if(this.labelWidth < 13 && this.labelmd == 0){
12979                 this.labelmd = this.labelWidth;
12980             }
12981             
12982             if(this.labellg > 0){
12983                 labelCfg.cls += ' col-lg-' + this.labellg;
12984                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12985             }
12986             
12987             if(this.labelmd > 0){
12988                 labelCfg.cls += ' col-md-' + this.labelmd;
12989                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12990             }
12991             
12992             if(this.labelsm > 0){
12993                 labelCfg.cls += ' col-sm-' + this.labelsm;
12994                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12995             }
12996             
12997             if(this.labelxs > 0){
12998                 labelCfg.cls += ' col-xs-' + this.labelxs;
12999                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13000             }
13001                 
13002                 
13003         } else if ( this.fieldLabel.length) {
13004 //                Roo.log(" label");
13005                  cfg.cn = [
13006                     {
13007                         tag : 'i',
13008                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13009                         tooltip : 'This field is required'
13010                     },
13011                     {
13012                         tag: 'label',
13013                         //cls : 'input-group-addon',
13014                         html : this.fieldLabel
13015                     },
13016                     combobox
13017                 ];
13018                 
13019                 if(this.indicatorpos == 'right'){
13020                     cfg.cn = [
13021                         {
13022                             tag: 'label',
13023                             //cls : 'input-group-addon',
13024                             html : this.fieldLabel
13025                         },
13026                         {
13027                             tag : 'i',
13028                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13029                             tooltip : 'This field is required'
13030                         },
13031                         combobox
13032                     ];
13033                     
13034                 }
13035
13036         } else {
13037             
13038 //                Roo.log(" no label && no align");
13039                 cfg = combobox
13040                      
13041                 
13042         }
13043          
13044         var settings=this;
13045         ['xs','sm','md','lg'].map(function(size){
13046             if (settings[size]) {
13047                 cfg.cls += ' col-' + size + '-' + settings[size];
13048             }
13049         });
13050         
13051         return cfg;
13052         
13053     },
13054     
13055     _initEventsCalled : false,
13056     
13057     // private
13058     initEvents: function()
13059     {   
13060         if (this._initEventsCalled) { // as we call render... prevent looping...
13061             return;
13062         }
13063         this._initEventsCalled = true;
13064         
13065         if (!this.store) {
13066             throw "can not find store for combo";
13067         }
13068         
13069         this.indicator = this.indicatorEl();
13070         
13071         this.store = Roo.factory(this.store, Roo.data);
13072         this.store.parent = this;
13073         
13074         // if we are building from html. then this element is so complex, that we can not really
13075         // use the rendered HTML.
13076         // so we have to trash and replace the previous code.
13077         if (Roo.XComponent.build_from_html) {
13078             // remove this element....
13079             var e = this.el.dom, k=0;
13080             while (e ) { e = e.previousSibling;  ++k;}
13081
13082             this.el.remove();
13083             
13084             this.el=false;
13085             this.rendered = false;
13086             
13087             this.render(this.parent().getChildContainer(true), k);
13088         }
13089         
13090         if(Roo.isIOS && this.useNativeIOS){
13091             this.initIOSView();
13092             return;
13093         }
13094         
13095         /*
13096          * Touch Devices
13097          */
13098         
13099         if(Roo.isTouch && this.mobileTouchView){
13100             this.initTouchView();
13101             return;
13102         }
13103         
13104         if(this.tickable){
13105             this.initTickableEvents();
13106             return;
13107         }
13108         
13109         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13110         
13111         if(this.hiddenName){
13112             
13113             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13114             
13115             this.hiddenField.dom.value =
13116                 this.hiddenValue !== undefined ? this.hiddenValue :
13117                 this.value !== undefined ? this.value : '';
13118
13119             // prevent input submission
13120             this.el.dom.removeAttribute('name');
13121             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13122              
13123              
13124         }
13125         //if(Roo.isGecko){
13126         //    this.el.dom.setAttribute('autocomplete', 'off');
13127         //}
13128         
13129         var cls = 'x-combo-list';
13130         
13131         //this.list = new Roo.Layer({
13132         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13133         //});
13134         
13135         var _this = this;
13136         
13137         (function(){
13138             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13139             _this.list.setWidth(lw);
13140         }).defer(100);
13141         
13142         this.list.on('mouseover', this.onViewOver, this);
13143         this.list.on('mousemove', this.onViewMove, this);
13144         this.list.on('scroll', this.onViewScroll, this);
13145         
13146         /*
13147         this.list.swallowEvent('mousewheel');
13148         this.assetHeight = 0;
13149
13150         if(this.title){
13151             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13152             this.assetHeight += this.header.getHeight();
13153         }
13154
13155         this.innerList = this.list.createChild({cls:cls+'-inner'});
13156         this.innerList.on('mouseover', this.onViewOver, this);
13157         this.innerList.on('mousemove', this.onViewMove, this);
13158         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13159         
13160         if(this.allowBlank && !this.pageSize && !this.disableClear){
13161             this.footer = this.list.createChild({cls:cls+'-ft'});
13162             this.pageTb = new Roo.Toolbar(this.footer);
13163            
13164         }
13165         if(this.pageSize){
13166             this.footer = this.list.createChild({cls:cls+'-ft'});
13167             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13168                     {pageSize: this.pageSize});
13169             
13170         }
13171         
13172         if (this.pageTb && this.allowBlank && !this.disableClear) {
13173             var _this = this;
13174             this.pageTb.add(new Roo.Toolbar.Fill(), {
13175                 cls: 'x-btn-icon x-btn-clear',
13176                 text: '&#160;',
13177                 handler: function()
13178                 {
13179                     _this.collapse();
13180                     _this.clearValue();
13181                     _this.onSelect(false, -1);
13182                 }
13183             });
13184         }
13185         if (this.footer) {
13186             this.assetHeight += this.footer.getHeight();
13187         }
13188         */
13189             
13190         if(!this.tpl){
13191             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13192         }
13193
13194         this.view = new Roo.View(this.list, this.tpl, {
13195             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13196         });
13197         //this.view.wrapEl.setDisplayed(false);
13198         this.view.on('click', this.onViewClick, this);
13199         
13200         
13201         this.store.on('beforeload', this.onBeforeLoad, this);
13202         this.store.on('load', this.onLoad, this);
13203         this.store.on('loadexception', this.onLoadException, this);
13204         /*
13205         if(this.resizable){
13206             this.resizer = new Roo.Resizable(this.list,  {
13207                pinned:true, handles:'se'
13208             });
13209             this.resizer.on('resize', function(r, w, h){
13210                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13211                 this.listWidth = w;
13212                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13213                 this.restrictHeight();
13214             }, this);
13215             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13216         }
13217         */
13218         if(!this.editable){
13219             this.editable = true;
13220             this.setEditable(false);
13221         }
13222         
13223         /*
13224         
13225         if (typeof(this.events.add.listeners) != 'undefined') {
13226             
13227             this.addicon = this.wrap.createChild(
13228                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13229        
13230             this.addicon.on('click', function(e) {
13231                 this.fireEvent('add', this);
13232             }, this);
13233         }
13234         if (typeof(this.events.edit.listeners) != 'undefined') {
13235             
13236             this.editicon = this.wrap.createChild(
13237                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13238             if (this.addicon) {
13239                 this.editicon.setStyle('margin-left', '40px');
13240             }
13241             this.editicon.on('click', function(e) {
13242                 
13243                 // we fire even  if inothing is selected..
13244                 this.fireEvent('edit', this, this.lastData );
13245                 
13246             }, this);
13247         }
13248         */
13249         
13250         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13251             "up" : function(e){
13252                 this.inKeyMode = true;
13253                 this.selectPrev();
13254             },
13255
13256             "down" : function(e){
13257                 if(!this.isExpanded()){
13258                     this.onTriggerClick();
13259                 }else{
13260                     this.inKeyMode = true;
13261                     this.selectNext();
13262                 }
13263             },
13264
13265             "enter" : function(e){
13266 //                this.onViewClick();
13267                 //return true;
13268                 this.collapse();
13269                 
13270                 if(this.fireEvent("specialkey", this, e)){
13271                     this.onViewClick(false);
13272                 }
13273                 
13274                 return true;
13275             },
13276
13277             "esc" : function(e){
13278                 this.collapse();
13279             },
13280
13281             "tab" : function(e){
13282                 this.collapse();
13283                 
13284                 if(this.fireEvent("specialkey", this, e)){
13285                     this.onViewClick(false);
13286                 }
13287                 
13288                 return true;
13289             },
13290
13291             scope : this,
13292
13293             doRelay : function(foo, bar, hname){
13294                 if(hname == 'down' || this.scope.isExpanded()){
13295                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13296                 }
13297                 return true;
13298             },
13299
13300             forceKeyDown: true
13301         });
13302         
13303         
13304         this.queryDelay = Math.max(this.queryDelay || 10,
13305                 this.mode == 'local' ? 10 : 250);
13306         
13307         
13308         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13309         
13310         if(this.typeAhead){
13311             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13312         }
13313         if(this.editable !== false){
13314             this.inputEl().on("keyup", this.onKeyUp, this);
13315         }
13316         if(this.forceSelection){
13317             this.inputEl().on('blur', this.doForce, this);
13318         }
13319         
13320         if(this.multiple){
13321             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13322             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13323         }
13324     },
13325     
13326     initTickableEvents: function()
13327     {   
13328         this.createList();
13329         
13330         if(this.hiddenName){
13331             
13332             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13333             
13334             this.hiddenField.dom.value =
13335                 this.hiddenValue !== undefined ? this.hiddenValue :
13336                 this.value !== undefined ? this.value : '';
13337
13338             // prevent input submission
13339             this.el.dom.removeAttribute('name');
13340             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13341              
13342              
13343         }
13344         
13345 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13346         
13347         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13348         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13349         if(this.triggerList){
13350             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13351         }
13352          
13353         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13354         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13355         
13356         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13357         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13358         
13359         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13360         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13361         
13362         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13363         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13364         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13365         
13366         this.okBtn.hide();
13367         this.cancelBtn.hide();
13368         
13369         var _this = this;
13370         
13371         (function(){
13372             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13373             _this.list.setWidth(lw);
13374         }).defer(100);
13375         
13376         this.list.on('mouseover', this.onViewOver, this);
13377         this.list.on('mousemove', this.onViewMove, this);
13378         
13379         this.list.on('scroll', this.onViewScroll, this);
13380         
13381         if(!this.tpl){
13382             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>';
13383         }
13384
13385         this.view = new Roo.View(this.list, this.tpl, {
13386             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13387         });
13388         
13389         //this.view.wrapEl.setDisplayed(false);
13390         this.view.on('click', this.onViewClick, this);
13391         
13392         
13393         
13394         this.store.on('beforeload', this.onBeforeLoad, this);
13395         this.store.on('load', this.onLoad, this);
13396         this.store.on('loadexception', this.onLoadException, this);
13397         
13398         if(this.editable){
13399             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13400                 "up" : function(e){
13401                     this.inKeyMode = true;
13402                     this.selectPrev();
13403                 },
13404
13405                 "down" : function(e){
13406                     this.inKeyMode = true;
13407                     this.selectNext();
13408                 },
13409
13410                 "enter" : function(e){
13411                     if(this.fireEvent("specialkey", this, e)){
13412                         this.onViewClick(false);
13413                     }
13414                     
13415                     return true;
13416                 },
13417
13418                 "esc" : function(e){
13419                     this.onTickableFooterButtonClick(e, false, false);
13420                 },
13421
13422                 "tab" : function(e){
13423                     this.fireEvent("specialkey", this, e);
13424                     
13425                     this.onTickableFooterButtonClick(e, false, false);
13426                     
13427                     return true;
13428                 },
13429
13430                 scope : this,
13431
13432                 doRelay : function(e, fn, key){
13433                     if(this.scope.isExpanded()){
13434                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13435                     }
13436                     return true;
13437                 },
13438
13439                 forceKeyDown: true
13440             });
13441         }
13442         
13443         this.queryDelay = Math.max(this.queryDelay || 10,
13444                 this.mode == 'local' ? 10 : 250);
13445         
13446         
13447         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13448         
13449         if(this.typeAhead){
13450             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13451         }
13452         
13453         if(this.editable !== false){
13454             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13455         }
13456         
13457         this.indicator = this.indicatorEl();
13458         
13459         if(this.indicator){
13460             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13461             this.indicator.hide();
13462         }
13463         
13464     },
13465
13466     onDestroy : function(){
13467         if(this.view){
13468             this.view.setStore(null);
13469             this.view.el.removeAllListeners();
13470             this.view.el.remove();
13471             this.view.purgeListeners();
13472         }
13473         if(this.list){
13474             this.list.dom.innerHTML  = '';
13475         }
13476         
13477         if(this.store){
13478             this.store.un('beforeload', this.onBeforeLoad, this);
13479             this.store.un('load', this.onLoad, this);
13480             this.store.un('loadexception', this.onLoadException, this);
13481         }
13482         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13483     },
13484
13485     // private
13486     fireKey : function(e){
13487         if(e.isNavKeyPress() && !this.list.isVisible()){
13488             this.fireEvent("specialkey", this, e);
13489         }
13490     },
13491
13492     // private
13493     onResize: function(w, h){
13494 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13495 //        
13496 //        if(typeof w != 'number'){
13497 //            // we do not handle it!?!?
13498 //            return;
13499 //        }
13500 //        var tw = this.trigger.getWidth();
13501 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13502 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13503 //        var x = w - tw;
13504 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13505 //            
13506 //        //this.trigger.setStyle('left', x+'px');
13507 //        
13508 //        if(this.list && this.listWidth === undefined){
13509 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13510 //            this.list.setWidth(lw);
13511 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13512 //        }
13513         
13514     
13515         
13516     },
13517
13518     /**
13519      * Allow or prevent the user from directly editing the field text.  If false is passed,
13520      * the user will only be able to select from the items defined in the dropdown list.  This method
13521      * is the runtime equivalent of setting the 'editable' config option at config time.
13522      * @param {Boolean} value True to allow the user to directly edit the field text
13523      */
13524     setEditable : function(value){
13525         if(value == this.editable){
13526             return;
13527         }
13528         this.editable = value;
13529         if(!value){
13530             this.inputEl().dom.setAttribute('readOnly', true);
13531             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13532             this.inputEl().addClass('x-combo-noedit');
13533         }else{
13534             this.inputEl().dom.setAttribute('readOnly', false);
13535             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13536             this.inputEl().removeClass('x-combo-noedit');
13537         }
13538     },
13539
13540     // private
13541     
13542     onBeforeLoad : function(combo,opts){
13543         if(!this.hasFocus){
13544             return;
13545         }
13546          if (!opts.add) {
13547             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13548          }
13549         this.restrictHeight();
13550         this.selectedIndex = -1;
13551     },
13552
13553     // private
13554     onLoad : function(){
13555         
13556         this.hasQuery = false;
13557         
13558         if(!this.hasFocus){
13559             return;
13560         }
13561         
13562         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13563             this.loading.hide();
13564         }
13565         
13566         if(this.store.getCount() > 0){
13567             
13568             this.expand();
13569             this.restrictHeight();
13570             if(this.lastQuery == this.allQuery){
13571                 if(this.editable && !this.tickable){
13572                     this.inputEl().dom.select();
13573                 }
13574                 
13575                 if(
13576                     !this.selectByValue(this.value, true) &&
13577                     this.autoFocus && 
13578                     (
13579                         !this.store.lastOptions ||
13580                         typeof(this.store.lastOptions.add) == 'undefined' || 
13581                         this.store.lastOptions.add != true
13582                     )
13583                 ){
13584                     this.select(0, true);
13585                 }
13586             }else{
13587                 if(this.autoFocus){
13588                     this.selectNext();
13589                 }
13590                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13591                     this.taTask.delay(this.typeAheadDelay);
13592                 }
13593             }
13594         }else{
13595             this.onEmptyResults();
13596         }
13597         
13598         //this.el.focus();
13599     },
13600     // private
13601     onLoadException : function()
13602     {
13603         this.hasQuery = false;
13604         
13605         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13606             this.loading.hide();
13607         }
13608         
13609         if(this.tickable && this.editable){
13610             return;
13611         }
13612         
13613         this.collapse();
13614         // only causes errors at present
13615         //Roo.log(this.store.reader.jsonData);
13616         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13617             // fixme
13618             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13619         //}
13620         
13621         
13622     },
13623     // private
13624     onTypeAhead : function(){
13625         if(this.store.getCount() > 0){
13626             var r = this.store.getAt(0);
13627             var newValue = r.data[this.displayField];
13628             var len = newValue.length;
13629             var selStart = this.getRawValue().length;
13630             
13631             if(selStart != len){
13632                 this.setRawValue(newValue);
13633                 this.selectText(selStart, newValue.length);
13634             }
13635         }
13636     },
13637
13638     // private
13639     onSelect : function(record, index){
13640         
13641         if(this.fireEvent('beforeselect', this, record, index) !== false){
13642         
13643             this.setFromData(index > -1 ? record.data : false);
13644             
13645             this.collapse();
13646             this.fireEvent('select', this, record, index);
13647         }
13648     },
13649
13650     /**
13651      * Returns the currently selected field value or empty string if no value is set.
13652      * @return {String} value The selected value
13653      */
13654     getValue : function()
13655     {
13656         if(Roo.isIOS && this.useNativeIOS){
13657             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13658         }
13659         
13660         if(this.multiple){
13661             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13662         }
13663         
13664         if(this.valueField){
13665             return typeof this.value != 'undefined' ? this.value : '';
13666         }else{
13667             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13668         }
13669     },
13670     
13671     getRawValue : function()
13672     {
13673         if(Roo.isIOS && this.useNativeIOS){
13674             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13675         }
13676         
13677         var v = this.inputEl().getValue();
13678         
13679         return v;
13680     },
13681
13682     /**
13683      * Clears any text/value currently set in the field
13684      */
13685     clearValue : function(){
13686         
13687         if(this.hiddenField){
13688             this.hiddenField.dom.value = '';
13689         }
13690         this.value = '';
13691         this.setRawValue('');
13692         this.lastSelectionText = '';
13693         this.lastData = false;
13694         
13695         var close = this.closeTriggerEl();
13696         
13697         if(close){
13698             close.hide();
13699         }
13700         
13701         this.validate();
13702         
13703     },
13704
13705     /**
13706      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13707      * will be displayed in the field.  If the value does not match the data value of an existing item,
13708      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13709      * Otherwise the field will be blank (although the value will still be set).
13710      * @param {String} value The value to match
13711      */
13712     setValue : function(v)
13713     {
13714         if(Roo.isIOS && this.useNativeIOS){
13715             this.setIOSValue(v);
13716             return;
13717         }
13718         
13719         if(this.multiple){
13720             this.syncValue();
13721             return;
13722         }
13723         
13724         var text = v;
13725         if(this.valueField){
13726             var r = this.findRecord(this.valueField, v);
13727             if(r){
13728                 text = r.data[this.displayField];
13729             }else if(this.valueNotFoundText !== undefined){
13730                 text = this.valueNotFoundText;
13731             }
13732         }
13733         this.lastSelectionText = text;
13734         if(this.hiddenField){
13735             this.hiddenField.dom.value = v;
13736         }
13737         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13738         this.value = v;
13739         
13740         var close = this.closeTriggerEl();
13741         
13742         if(close){
13743             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13744         }
13745         
13746         this.validate();
13747     },
13748     /**
13749      * @property {Object} the last set data for the element
13750      */
13751     
13752     lastData : false,
13753     /**
13754      * Sets the value of the field based on a object which is related to the record format for the store.
13755      * @param {Object} value the value to set as. or false on reset?
13756      */
13757     setFromData : function(o){
13758         
13759         if(this.multiple){
13760             this.addItem(o);
13761             return;
13762         }
13763             
13764         var dv = ''; // display value
13765         var vv = ''; // value value..
13766         this.lastData = o;
13767         if (this.displayField) {
13768             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13769         } else {
13770             // this is an error condition!!!
13771             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13772         }
13773         
13774         if(this.valueField){
13775             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13776         }
13777         
13778         var close = this.closeTriggerEl();
13779         
13780         if(close){
13781             if(dv.length || vv * 1 > 0){
13782                 close.show() ;
13783                 this.blockFocus=true;
13784             } else {
13785                 close.hide();
13786             }             
13787         }
13788         
13789         if(this.hiddenField){
13790             this.hiddenField.dom.value = vv;
13791             
13792             this.lastSelectionText = dv;
13793             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13794             this.value = vv;
13795             return;
13796         }
13797         // no hidden field.. - we store the value in 'value', but still display
13798         // display field!!!!
13799         this.lastSelectionText = dv;
13800         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13801         this.value = vv;
13802         
13803         
13804         
13805     },
13806     // private
13807     reset : function(){
13808         // overridden so that last data is reset..
13809         
13810         if(this.multiple){
13811             this.clearItem();
13812             return;
13813         }
13814         
13815         this.setValue(this.originalValue);
13816         //this.clearInvalid();
13817         this.lastData = false;
13818         if (this.view) {
13819             this.view.clearSelections();
13820         }
13821         
13822         this.validate();
13823     },
13824     // private
13825     findRecord : function(prop, value){
13826         var record;
13827         if(this.store.getCount() > 0){
13828             this.store.each(function(r){
13829                 if(r.data[prop] == value){
13830                     record = r;
13831                     return false;
13832                 }
13833                 return true;
13834             });
13835         }
13836         return record;
13837     },
13838     
13839     getName: function()
13840     {
13841         // returns hidden if it's set..
13842         if (!this.rendered) {return ''};
13843         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13844         
13845     },
13846     // private
13847     onViewMove : function(e, t){
13848         this.inKeyMode = false;
13849     },
13850
13851     // private
13852     onViewOver : function(e, t){
13853         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13854             return;
13855         }
13856         var item = this.view.findItemFromChild(t);
13857         
13858         if(item){
13859             var index = this.view.indexOf(item);
13860             this.select(index, false);
13861         }
13862     },
13863
13864     // private
13865     onViewClick : function(view, doFocus, el, e)
13866     {
13867         var index = this.view.getSelectedIndexes()[0];
13868         
13869         var r = this.store.getAt(index);
13870         
13871         if(this.tickable){
13872             
13873             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13874                 return;
13875             }
13876             
13877             var rm = false;
13878             var _this = this;
13879             
13880             Roo.each(this.tickItems, function(v,k){
13881                 
13882                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13883                     Roo.log(v);
13884                     _this.tickItems.splice(k, 1);
13885                     
13886                     if(typeof(e) == 'undefined' && view == false){
13887                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13888                     }
13889                     
13890                     rm = true;
13891                     return;
13892                 }
13893             });
13894             
13895             if(rm){
13896                 return;
13897             }
13898             
13899             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13900                 this.tickItems.push(r.data);
13901             }
13902             
13903             if(typeof(e) == 'undefined' && view == false){
13904                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13905             }
13906                     
13907             return;
13908         }
13909         
13910         if(r){
13911             this.onSelect(r, index);
13912         }
13913         if(doFocus !== false && !this.blockFocus){
13914             this.inputEl().focus();
13915         }
13916     },
13917
13918     // private
13919     restrictHeight : function(){
13920         //this.innerList.dom.style.height = '';
13921         //var inner = this.innerList.dom;
13922         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13923         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13924         //this.list.beginUpdate();
13925         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13926         this.list.alignTo(this.inputEl(), this.listAlign);
13927         this.list.alignTo(this.inputEl(), this.listAlign);
13928         //this.list.endUpdate();
13929     },
13930
13931     // private
13932     onEmptyResults : function(){
13933         
13934         if(this.tickable && this.editable){
13935             this.restrictHeight();
13936             return;
13937         }
13938         
13939         this.collapse();
13940     },
13941
13942     /**
13943      * Returns true if the dropdown list is expanded, else false.
13944      */
13945     isExpanded : function(){
13946         return this.list.isVisible();
13947     },
13948
13949     /**
13950      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13951      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13952      * @param {String} value The data value of the item to select
13953      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13954      * selected item if it is not currently in view (defaults to true)
13955      * @return {Boolean} True if the value matched an item in the list, else false
13956      */
13957     selectByValue : function(v, scrollIntoView){
13958         if(v !== undefined && v !== null){
13959             var r = this.findRecord(this.valueField || this.displayField, v);
13960             if(r){
13961                 this.select(this.store.indexOf(r), scrollIntoView);
13962                 return true;
13963             }
13964         }
13965         return false;
13966     },
13967
13968     /**
13969      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13970      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13971      * @param {Number} index The zero-based index of the list item to select
13972      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13973      * selected item if it is not currently in view (defaults to true)
13974      */
13975     select : function(index, scrollIntoView){
13976         this.selectedIndex = index;
13977         this.view.select(index);
13978         if(scrollIntoView !== false){
13979             var el = this.view.getNode(index);
13980             /*
13981              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13982              */
13983             if(el){
13984                 this.list.scrollChildIntoView(el, false);
13985             }
13986         }
13987     },
13988
13989     // private
13990     selectNext : function(){
13991         var ct = this.store.getCount();
13992         if(ct > 0){
13993             if(this.selectedIndex == -1){
13994                 this.select(0);
13995             }else if(this.selectedIndex < ct-1){
13996                 this.select(this.selectedIndex+1);
13997             }
13998         }
13999     },
14000
14001     // private
14002     selectPrev : function(){
14003         var ct = this.store.getCount();
14004         if(ct > 0){
14005             if(this.selectedIndex == -1){
14006                 this.select(0);
14007             }else if(this.selectedIndex != 0){
14008                 this.select(this.selectedIndex-1);
14009             }
14010         }
14011     },
14012
14013     // private
14014     onKeyUp : function(e){
14015         if(this.editable !== false && !e.isSpecialKey()){
14016             this.lastKey = e.getKey();
14017             this.dqTask.delay(this.queryDelay);
14018         }
14019     },
14020
14021     // private
14022     validateBlur : function(){
14023         return !this.list || !this.list.isVisible();   
14024     },
14025
14026     // private
14027     initQuery : function(){
14028         
14029         var v = this.getRawValue();
14030         
14031         if(this.tickable && this.editable){
14032             v = this.tickableInputEl().getValue();
14033         }
14034         
14035         this.doQuery(v);
14036     },
14037
14038     // private
14039     doForce : function(){
14040         if(this.inputEl().dom.value.length > 0){
14041             this.inputEl().dom.value =
14042                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14043              
14044         }
14045     },
14046
14047     /**
14048      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14049      * query allowing the query action to be canceled if needed.
14050      * @param {String} query The SQL query to execute
14051      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14052      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14053      * saved in the current store (defaults to false)
14054      */
14055     doQuery : function(q, forceAll){
14056         
14057         if(q === undefined || q === null){
14058             q = '';
14059         }
14060         var qe = {
14061             query: q,
14062             forceAll: forceAll,
14063             combo: this,
14064             cancel:false
14065         };
14066         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14067             return false;
14068         }
14069         q = qe.query;
14070         
14071         forceAll = qe.forceAll;
14072         if(forceAll === true || (q.length >= this.minChars)){
14073             
14074             this.hasQuery = true;
14075             
14076             if(this.lastQuery != q || this.alwaysQuery){
14077                 this.lastQuery = q;
14078                 if(this.mode == 'local'){
14079                     this.selectedIndex = -1;
14080                     if(forceAll){
14081                         this.store.clearFilter();
14082                     }else{
14083                         
14084                         if(this.specialFilter){
14085                             this.fireEvent('specialfilter', this);
14086                             this.onLoad();
14087                             return;
14088                         }
14089                         
14090                         this.store.filter(this.displayField, q);
14091                     }
14092                     
14093                     this.store.fireEvent("datachanged", this.store);
14094                     
14095                     this.onLoad();
14096                     
14097                     
14098                 }else{
14099                     
14100                     this.store.baseParams[this.queryParam] = q;
14101                     
14102                     var options = {params : this.getParams(q)};
14103                     
14104                     if(this.loadNext){
14105                         options.add = true;
14106                         options.params.start = this.page * this.pageSize;
14107                     }
14108                     
14109                     this.store.load(options);
14110                     
14111                     /*
14112                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14113                      *  we should expand the list on onLoad
14114                      *  so command out it
14115                      */
14116 //                    this.expand();
14117                 }
14118             }else{
14119                 this.selectedIndex = -1;
14120                 this.onLoad();   
14121             }
14122         }
14123         
14124         this.loadNext = false;
14125     },
14126     
14127     // private
14128     getParams : function(q){
14129         var p = {};
14130         //p[this.queryParam] = q;
14131         
14132         if(this.pageSize){
14133             p.start = 0;
14134             p.limit = this.pageSize;
14135         }
14136         return p;
14137     },
14138
14139     /**
14140      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14141      */
14142     collapse : function(){
14143         if(!this.isExpanded()){
14144             return;
14145         }
14146         
14147         this.list.hide();
14148         
14149         this.hasFocus = false;
14150         
14151         if(this.tickable){
14152             this.okBtn.hide();
14153             this.cancelBtn.hide();
14154             this.trigger.show();
14155             
14156             if(this.editable){
14157                 this.tickableInputEl().dom.value = '';
14158                 this.tickableInputEl().blur();
14159             }
14160             
14161         }
14162         
14163         Roo.get(document).un('mousedown', this.collapseIf, this);
14164         Roo.get(document).un('mousewheel', this.collapseIf, this);
14165         if (!this.editable) {
14166             Roo.get(document).un('keydown', this.listKeyPress, this);
14167         }
14168         this.fireEvent('collapse', this);
14169         
14170         this.validate();
14171     },
14172
14173     // private
14174     collapseIf : function(e){
14175         var in_combo  = e.within(this.el);
14176         var in_list =  e.within(this.list);
14177         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14178         
14179         if (in_combo || in_list || is_list) {
14180             //e.stopPropagation();
14181             return;
14182         }
14183         
14184         if(this.tickable){
14185             this.onTickableFooterButtonClick(e, false, false);
14186         }
14187
14188         this.collapse();
14189         
14190     },
14191
14192     /**
14193      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14194      */
14195     expand : function(){
14196        
14197         if(this.isExpanded() || !this.hasFocus){
14198             return;
14199         }
14200         
14201         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14202         this.list.setWidth(lw);
14203         
14204         Roo.log('expand');
14205         
14206         this.list.show();
14207         
14208         this.restrictHeight();
14209         
14210         if(this.tickable){
14211             
14212             this.tickItems = Roo.apply([], this.item);
14213             
14214             this.okBtn.show();
14215             this.cancelBtn.show();
14216             this.trigger.hide();
14217             
14218             if(this.editable){
14219                 this.tickableInputEl().focus();
14220             }
14221             
14222         }
14223         
14224         Roo.get(document).on('mousedown', this.collapseIf, this);
14225         Roo.get(document).on('mousewheel', this.collapseIf, this);
14226         if (!this.editable) {
14227             Roo.get(document).on('keydown', this.listKeyPress, this);
14228         }
14229         
14230         this.fireEvent('expand', this);
14231     },
14232
14233     // private
14234     // Implements the default empty TriggerField.onTriggerClick function
14235     onTriggerClick : function(e)
14236     {
14237         Roo.log('trigger click');
14238         
14239         if(this.disabled || !this.triggerList){
14240             return;
14241         }
14242         
14243         this.page = 0;
14244         this.loadNext = false;
14245         
14246         if(this.isExpanded()){
14247             this.collapse();
14248             if (!this.blockFocus) {
14249                 this.inputEl().focus();
14250             }
14251             
14252         }else {
14253             this.hasFocus = true;
14254             if(this.triggerAction == 'all') {
14255                 this.doQuery(this.allQuery, true);
14256             } else {
14257                 this.doQuery(this.getRawValue());
14258             }
14259             if (!this.blockFocus) {
14260                 this.inputEl().focus();
14261             }
14262         }
14263     },
14264     
14265     onTickableTriggerClick : function(e)
14266     {
14267         if(this.disabled){
14268             return;
14269         }
14270         
14271         this.page = 0;
14272         this.loadNext = false;
14273         this.hasFocus = true;
14274         
14275         if(this.triggerAction == 'all') {
14276             this.doQuery(this.allQuery, true);
14277         } else {
14278             this.doQuery(this.getRawValue());
14279         }
14280     },
14281     
14282     onSearchFieldClick : function(e)
14283     {
14284         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14285             this.onTickableFooterButtonClick(e, false, false);
14286             return;
14287         }
14288         
14289         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14290             return;
14291         }
14292         
14293         this.page = 0;
14294         this.loadNext = false;
14295         this.hasFocus = true;
14296         
14297         if(this.triggerAction == 'all') {
14298             this.doQuery(this.allQuery, true);
14299         } else {
14300             this.doQuery(this.getRawValue());
14301         }
14302     },
14303     
14304     listKeyPress : function(e)
14305     {
14306         //Roo.log('listkeypress');
14307         // scroll to first matching element based on key pres..
14308         if (e.isSpecialKey()) {
14309             return false;
14310         }
14311         var k = String.fromCharCode(e.getKey()).toUpperCase();
14312         //Roo.log(k);
14313         var match  = false;
14314         var csel = this.view.getSelectedNodes();
14315         var cselitem = false;
14316         if (csel.length) {
14317             var ix = this.view.indexOf(csel[0]);
14318             cselitem  = this.store.getAt(ix);
14319             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14320                 cselitem = false;
14321             }
14322             
14323         }
14324         
14325         this.store.each(function(v) { 
14326             if (cselitem) {
14327                 // start at existing selection.
14328                 if (cselitem.id == v.id) {
14329                     cselitem = false;
14330                 }
14331                 return true;
14332             }
14333                 
14334             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14335                 match = this.store.indexOf(v);
14336                 return false;
14337             }
14338             return true;
14339         }, this);
14340         
14341         if (match === false) {
14342             return true; // no more action?
14343         }
14344         // scroll to?
14345         this.view.select(match);
14346         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14347         sn.scrollIntoView(sn.dom.parentNode, false);
14348     },
14349     
14350     onViewScroll : function(e, t){
14351         
14352         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){
14353             return;
14354         }
14355         
14356         this.hasQuery = true;
14357         
14358         this.loading = this.list.select('.loading', true).first();
14359         
14360         if(this.loading === null){
14361             this.list.createChild({
14362                 tag: 'div',
14363                 cls: 'loading roo-select2-more-results roo-select2-active',
14364                 html: 'Loading more results...'
14365             });
14366             
14367             this.loading = this.list.select('.loading', true).first();
14368             
14369             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14370             
14371             this.loading.hide();
14372         }
14373         
14374         this.loading.show();
14375         
14376         var _combo = this;
14377         
14378         this.page++;
14379         this.loadNext = true;
14380         
14381         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14382         
14383         return;
14384     },
14385     
14386     addItem : function(o)
14387     {   
14388         var dv = ''; // display value
14389         
14390         if (this.displayField) {
14391             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14392         } else {
14393             // this is an error condition!!!
14394             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14395         }
14396         
14397         if(!dv.length){
14398             return;
14399         }
14400         
14401         var choice = this.choices.createChild({
14402             tag: 'li',
14403             cls: 'roo-select2-search-choice',
14404             cn: [
14405                 {
14406                     tag: 'div',
14407                     html: dv
14408                 },
14409                 {
14410                     tag: 'a',
14411                     href: '#',
14412                     cls: 'roo-select2-search-choice-close fa fa-times',
14413                     tabindex: '-1'
14414                 }
14415             ]
14416             
14417         }, this.searchField);
14418         
14419         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14420         
14421         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14422         
14423         this.item.push(o);
14424         
14425         this.lastData = o;
14426         
14427         this.syncValue();
14428         
14429         this.inputEl().dom.value = '';
14430         
14431         this.validate();
14432     },
14433     
14434     onRemoveItem : function(e, _self, o)
14435     {
14436         e.preventDefault();
14437         
14438         this.lastItem = Roo.apply([], this.item);
14439         
14440         var index = this.item.indexOf(o.data) * 1;
14441         
14442         if( index < 0){
14443             Roo.log('not this item?!');
14444             return;
14445         }
14446         
14447         this.item.splice(index, 1);
14448         o.item.remove();
14449         
14450         this.syncValue();
14451         
14452         this.fireEvent('remove', this, e);
14453         
14454         this.validate();
14455         
14456     },
14457     
14458     syncValue : function()
14459     {
14460         if(!this.item.length){
14461             this.clearValue();
14462             return;
14463         }
14464             
14465         var value = [];
14466         var _this = this;
14467         Roo.each(this.item, function(i){
14468             if(_this.valueField){
14469                 value.push(i[_this.valueField]);
14470                 return;
14471             }
14472
14473             value.push(i);
14474         });
14475
14476         this.value = value.join(',');
14477
14478         if(this.hiddenField){
14479             this.hiddenField.dom.value = this.value;
14480         }
14481         
14482         this.store.fireEvent("datachanged", this.store);
14483         
14484         this.validate();
14485     },
14486     
14487     clearItem : function()
14488     {
14489         if(!this.multiple){
14490             return;
14491         }
14492         
14493         this.item = [];
14494         
14495         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14496            c.remove();
14497         });
14498         
14499         this.syncValue();
14500         
14501         this.validate();
14502         
14503         if(this.tickable && !Roo.isTouch){
14504             this.view.refresh();
14505         }
14506     },
14507     
14508     inputEl: function ()
14509     {
14510         if(Roo.isIOS && this.useNativeIOS){
14511             return this.el.select('select.roo-ios-select', true).first();
14512         }
14513         
14514         if(Roo.isTouch && this.mobileTouchView){
14515             return this.el.select('input.form-control',true).first();
14516         }
14517         
14518         if(this.tickable){
14519             return this.searchField;
14520         }
14521         
14522         return this.el.select('input.form-control',true).first();
14523     },
14524     
14525     onTickableFooterButtonClick : function(e, btn, el)
14526     {
14527         e.preventDefault();
14528         
14529         this.lastItem = Roo.apply([], this.item);
14530         
14531         if(btn && btn.name == 'cancel'){
14532             this.tickItems = Roo.apply([], this.item);
14533             this.collapse();
14534             return;
14535         }
14536         
14537         this.clearItem();
14538         
14539         var _this = this;
14540         
14541         Roo.each(this.tickItems, function(o){
14542             _this.addItem(o);
14543         });
14544         
14545         this.collapse();
14546         
14547     },
14548     
14549     validate : function()
14550     {
14551         var v = this.getRawValue();
14552         
14553         if(this.multiple){
14554             v = this.getValue();
14555         }
14556         
14557         if(this.disabled || this.allowBlank || v.length){
14558             this.markValid();
14559             return true;
14560         }
14561         
14562         this.markInvalid();
14563         return false;
14564     },
14565     
14566     tickableInputEl : function()
14567     {
14568         if(!this.tickable || !this.editable){
14569             return this.inputEl();
14570         }
14571         
14572         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14573     },
14574     
14575     
14576     getAutoCreateTouchView : function()
14577     {
14578         var id = Roo.id();
14579         
14580         var cfg = {
14581             cls: 'form-group' //input-group
14582         };
14583         
14584         var input =  {
14585             tag: 'input',
14586             id : id,
14587             type : this.inputType,
14588             cls : 'form-control x-combo-noedit',
14589             autocomplete: 'new-password',
14590             placeholder : this.placeholder || '',
14591             readonly : true
14592         };
14593         
14594         if (this.name) {
14595             input.name = this.name;
14596         }
14597         
14598         if (this.size) {
14599             input.cls += ' input-' + this.size;
14600         }
14601         
14602         if (this.disabled) {
14603             input.disabled = true;
14604         }
14605         
14606         var inputblock = {
14607             cls : '',
14608             cn : [
14609                 input
14610             ]
14611         };
14612         
14613         if(this.before){
14614             inputblock.cls += ' input-group';
14615             
14616             inputblock.cn.unshift({
14617                 tag :'span',
14618                 cls : 'input-group-addon',
14619                 html : this.before
14620             });
14621         }
14622         
14623         if(this.removable && !this.multiple){
14624             inputblock.cls += ' roo-removable';
14625             
14626             inputblock.cn.push({
14627                 tag: 'button',
14628                 html : 'x',
14629                 cls : 'roo-combo-removable-btn close'
14630             });
14631         }
14632
14633         if(this.hasFeedback && !this.allowBlank){
14634             
14635             inputblock.cls += ' has-feedback';
14636             
14637             inputblock.cn.push({
14638                 tag: 'span',
14639                 cls: 'glyphicon form-control-feedback'
14640             });
14641             
14642         }
14643         
14644         if (this.after) {
14645             
14646             inputblock.cls += (this.before) ? '' : ' input-group';
14647             
14648             inputblock.cn.push({
14649                 tag :'span',
14650                 cls : 'input-group-addon',
14651                 html : this.after
14652             });
14653         }
14654
14655         var box = {
14656             tag: 'div',
14657             cn: [
14658                 {
14659                     tag: 'input',
14660                     type : 'hidden',
14661                     cls: 'form-hidden-field'
14662                 },
14663                 inputblock
14664             ]
14665             
14666         };
14667         
14668         if(this.multiple){
14669             box = {
14670                 tag: 'div',
14671                 cn: [
14672                     {
14673                         tag: 'input',
14674                         type : 'hidden',
14675                         cls: 'form-hidden-field'
14676                     },
14677                     {
14678                         tag: 'ul',
14679                         cls: 'roo-select2-choices',
14680                         cn:[
14681                             {
14682                                 tag: 'li',
14683                                 cls: 'roo-select2-search-field',
14684                                 cn: [
14685
14686                                     inputblock
14687                                 ]
14688                             }
14689                         ]
14690                     }
14691                 ]
14692             }
14693         };
14694         
14695         var combobox = {
14696             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14697             cn: [
14698                 box
14699             ]
14700         };
14701         
14702         if(!this.multiple && this.showToggleBtn){
14703             
14704             var caret = {
14705                         tag: 'span',
14706                         cls: 'caret'
14707             };
14708             
14709             if (this.caret != false) {
14710                 caret = {
14711                      tag: 'i',
14712                      cls: 'fa fa-' + this.caret
14713                 };
14714                 
14715             }
14716             
14717             combobox.cn.push({
14718                 tag :'span',
14719                 cls : 'input-group-addon btn dropdown-toggle',
14720                 cn : [
14721                     caret,
14722                     {
14723                         tag: 'span',
14724                         cls: 'combobox-clear',
14725                         cn  : [
14726                             {
14727                                 tag : 'i',
14728                                 cls: 'icon-remove'
14729                             }
14730                         ]
14731                     }
14732                 ]
14733
14734             })
14735         }
14736         
14737         if(this.multiple){
14738             combobox.cls += ' roo-select2-container-multi';
14739         }
14740         
14741         var align = this.labelAlign || this.parentLabelAlign();
14742         
14743         if (align ==='left' && this.fieldLabel.length) {
14744
14745             cfg.cn = [
14746                 {
14747                    tag : 'i',
14748                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14749                    tooltip : 'This field is required'
14750                 },
14751                 {
14752                     tag: 'label',
14753                     cls : 'control-label',
14754                     html : this.fieldLabel
14755
14756                 },
14757                 {
14758                     cls : '', 
14759                     cn: [
14760                         combobox
14761                     ]
14762                 }
14763             ];
14764             
14765             var labelCfg = cfg.cn[1];
14766             var contentCfg = cfg.cn[2];
14767             
14768
14769             if(this.indicatorpos == 'right'){
14770                 cfg.cn = [
14771                     {
14772                         tag: 'label',
14773                         'for' :  id,
14774                         cls : 'control-label',
14775                         cn : [
14776                             {
14777                                 tag : 'span',
14778                                 html : this.fieldLabel
14779                             },
14780                             {
14781                                 tag : 'i',
14782                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14783                                 tooltip : 'This field is required'
14784                             }
14785                         ]
14786                     },
14787                     {
14788                         cls : "",
14789                         cn: [
14790                             combobox
14791                         ]
14792                     }
14793
14794                 ];
14795                 
14796                 labelCfg = cfg.cn[0];
14797                 contentCfg = cfg.cn[1];
14798             }
14799             
14800            
14801             
14802             if(this.labelWidth > 12){
14803                 labelCfg.style = "width: " + this.labelWidth + 'px';
14804             }
14805             
14806             if(this.labelWidth < 13 && this.labelmd == 0){
14807                 this.labelmd = this.labelWidth;
14808             }
14809             
14810             if(this.labellg > 0){
14811                 labelCfg.cls += ' col-lg-' + this.labellg;
14812                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14813             }
14814             
14815             if(this.labelmd > 0){
14816                 labelCfg.cls += ' col-md-' + this.labelmd;
14817                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14818             }
14819             
14820             if(this.labelsm > 0){
14821                 labelCfg.cls += ' col-sm-' + this.labelsm;
14822                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14823             }
14824             
14825             if(this.labelxs > 0){
14826                 labelCfg.cls += ' col-xs-' + this.labelxs;
14827                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14828             }
14829                 
14830                 
14831         } else if ( this.fieldLabel.length) {
14832             cfg.cn = [
14833                 {
14834                    tag : 'i',
14835                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14836                    tooltip : 'This field is required'
14837                 },
14838                 {
14839                     tag: 'label',
14840                     cls : 'control-label',
14841                     html : this.fieldLabel
14842
14843                 },
14844                 {
14845                     cls : '', 
14846                     cn: [
14847                         combobox
14848                     ]
14849                 }
14850             ];
14851             
14852             if(this.indicatorpos == 'right'){
14853                 cfg.cn = [
14854                     {
14855                         tag: 'label',
14856                         cls : 'control-label',
14857                         html : this.fieldLabel,
14858                         cn : [
14859                             {
14860                                tag : 'i',
14861                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14862                                tooltip : 'This field is required'
14863                             }
14864                         ]
14865                     },
14866                     {
14867                         cls : '', 
14868                         cn: [
14869                             combobox
14870                         ]
14871                     }
14872                 ];
14873             }
14874         } else {
14875             cfg.cn = combobox;    
14876         }
14877         
14878         
14879         var settings = this;
14880         
14881         ['xs','sm','md','lg'].map(function(size){
14882             if (settings[size]) {
14883                 cfg.cls += ' col-' + size + '-' + settings[size];
14884             }
14885         });
14886         
14887         return cfg;
14888     },
14889     
14890     initTouchView : function()
14891     {
14892         this.renderTouchView();
14893         
14894         this.touchViewEl.on('scroll', function(){
14895             this.el.dom.scrollTop = 0;
14896         }, this);
14897         
14898         this.originalValue = this.getValue();
14899         
14900         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14901         
14902         this.inputEl().on("click", this.showTouchView, this);
14903         if (this.triggerEl) {
14904             this.triggerEl.on("click", this.showTouchView, this);
14905         }
14906         
14907         
14908         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14909         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14910         
14911         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14912         
14913         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14914         this.store.on('load', this.onTouchViewLoad, this);
14915         this.store.on('loadexception', this.onTouchViewLoadException, this);
14916         
14917         if(this.hiddenName){
14918             
14919             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14920             
14921             this.hiddenField.dom.value =
14922                 this.hiddenValue !== undefined ? this.hiddenValue :
14923                 this.value !== undefined ? this.value : '';
14924         
14925             this.el.dom.removeAttribute('name');
14926             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14927         }
14928         
14929         if(this.multiple){
14930             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14931             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14932         }
14933         
14934         if(this.removable && !this.multiple){
14935             var close = this.closeTriggerEl();
14936             if(close){
14937                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14938                 close.on('click', this.removeBtnClick, this, close);
14939             }
14940         }
14941         /*
14942          * fix the bug in Safari iOS8
14943          */
14944         this.inputEl().on("focus", function(e){
14945             document.activeElement.blur();
14946         }, this);
14947         
14948         return;
14949         
14950         
14951     },
14952     
14953     renderTouchView : function()
14954     {
14955         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14956         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14957         
14958         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14959         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14960         
14961         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14962         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14963         this.touchViewBodyEl.setStyle('overflow', 'auto');
14964         
14965         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14966         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14967         
14968         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14969         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14970         
14971     },
14972     
14973     showTouchView : function()
14974     {
14975         if(this.disabled){
14976             return;
14977         }
14978         
14979         this.touchViewHeaderEl.hide();
14980
14981         if(this.modalTitle.length){
14982             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14983             this.touchViewHeaderEl.show();
14984         }
14985
14986         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14987         this.touchViewEl.show();
14988
14989         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14990         
14991         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14992         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14993
14994         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14995
14996         if(this.modalTitle.length){
14997             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14998         }
14999         
15000         this.touchViewBodyEl.setHeight(bodyHeight);
15001
15002         if(this.animate){
15003             var _this = this;
15004             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15005         }else{
15006             this.touchViewEl.addClass('in');
15007         }
15008
15009         this.doTouchViewQuery();
15010         
15011     },
15012     
15013     hideTouchView : function()
15014     {
15015         this.touchViewEl.removeClass('in');
15016
15017         if(this.animate){
15018             var _this = this;
15019             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15020         }else{
15021             this.touchViewEl.setStyle('display', 'none');
15022         }
15023         
15024     },
15025     
15026     setTouchViewValue : function()
15027     {
15028         if(this.multiple){
15029             this.clearItem();
15030         
15031             var _this = this;
15032
15033             Roo.each(this.tickItems, function(o){
15034                 this.addItem(o);
15035             }, this);
15036         }
15037         
15038         this.hideTouchView();
15039     },
15040     
15041     doTouchViewQuery : function()
15042     {
15043         var qe = {
15044             query: '',
15045             forceAll: true,
15046             combo: this,
15047             cancel:false
15048         };
15049         
15050         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15051             return false;
15052         }
15053         
15054         if(!this.alwaysQuery || this.mode == 'local'){
15055             this.onTouchViewLoad();
15056             return;
15057         }
15058         
15059         this.store.load();
15060     },
15061     
15062     onTouchViewBeforeLoad : function(combo,opts)
15063     {
15064         return;
15065     },
15066
15067     // private
15068     onTouchViewLoad : function()
15069     {
15070         if(this.store.getCount() < 1){
15071             this.onTouchViewEmptyResults();
15072             return;
15073         }
15074         
15075         this.clearTouchView();
15076         
15077         var rawValue = this.getRawValue();
15078         
15079         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15080         
15081         this.tickItems = [];
15082         
15083         this.store.data.each(function(d, rowIndex){
15084             var row = this.touchViewListGroup.createChild(template);
15085             
15086             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15087                 row.addClass(d.data.cls);
15088             }
15089             
15090             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15091                 var cfg = {
15092                     data : d.data,
15093                     html : d.data[this.displayField]
15094                 };
15095                 
15096                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15097                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15098                 }
15099             }
15100             row.removeClass('selected');
15101             if(!this.multiple && this.valueField &&
15102                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15103             {
15104                 // radio buttons..
15105                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15106                 row.addClass('selected');
15107             }
15108             
15109             if(this.multiple && this.valueField &&
15110                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15111             {
15112                 
15113                 // checkboxes...
15114                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15115                 this.tickItems.push(d.data);
15116             }
15117             
15118             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15119             
15120         }, this);
15121         
15122         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15123         
15124         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15125
15126         if(this.modalTitle.length){
15127             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15128         }
15129
15130         var listHeight = this.touchViewListGroup.getHeight();
15131         
15132         var _this = this;
15133         
15134         if(firstChecked && listHeight > bodyHeight){
15135             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15136         }
15137         
15138     },
15139     
15140     onTouchViewLoadException : function()
15141     {
15142         this.hideTouchView();
15143     },
15144     
15145     onTouchViewEmptyResults : function()
15146     {
15147         this.clearTouchView();
15148         
15149         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15150         
15151         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15152         
15153     },
15154     
15155     clearTouchView : function()
15156     {
15157         this.touchViewListGroup.dom.innerHTML = '';
15158     },
15159     
15160     onTouchViewClick : function(e, el, o)
15161     {
15162         e.preventDefault();
15163         
15164         var row = o.row;
15165         var rowIndex = o.rowIndex;
15166         
15167         var r = this.store.getAt(rowIndex);
15168         
15169         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15170             
15171             if(!this.multiple){
15172                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15173                     c.dom.removeAttribute('checked');
15174                 }, this);
15175
15176                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15177
15178                 this.setFromData(r.data);
15179
15180                 var close = this.closeTriggerEl();
15181
15182                 if(close){
15183                     close.show();
15184                 }
15185
15186                 this.hideTouchView();
15187
15188                 this.fireEvent('select', this, r, rowIndex);
15189
15190                 return;
15191             }
15192
15193             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15194                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15195                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15196                 return;
15197             }
15198
15199             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15200             this.addItem(r.data);
15201             this.tickItems.push(r.data);
15202         }
15203     },
15204     
15205     getAutoCreateNativeIOS : function()
15206     {
15207         var cfg = {
15208             cls: 'form-group' //input-group,
15209         };
15210         
15211         var combobox =  {
15212             tag: 'select',
15213             cls : 'roo-ios-select'
15214         };
15215         
15216         if (this.name) {
15217             combobox.name = this.name;
15218         }
15219         
15220         if (this.disabled) {
15221             combobox.disabled = true;
15222         }
15223         
15224         var settings = this;
15225         
15226         ['xs','sm','md','lg'].map(function(size){
15227             if (settings[size]) {
15228                 cfg.cls += ' col-' + size + '-' + settings[size];
15229             }
15230         });
15231         
15232         cfg.cn = combobox;
15233         
15234         return cfg;
15235         
15236     },
15237     
15238     initIOSView : function()
15239     {
15240         this.store.on('load', this.onIOSViewLoad, this);
15241         
15242         return;
15243     },
15244     
15245     onIOSViewLoad : function()
15246     {
15247         if(this.store.getCount() < 1){
15248             return;
15249         }
15250         
15251         this.clearIOSView();
15252         
15253         if(this.allowBlank) {
15254             
15255             var default_text = '-- SELECT --';
15256             
15257             if(this.placeholder.length){
15258                 default_text = this.placeholder;
15259             }
15260             
15261             if(this.emptyTitle.length){
15262                 default_text += ' - ' + this.emptyTitle + ' -';
15263             }
15264             
15265             var opt = this.inputEl().createChild({
15266                 tag: 'option',
15267                 value : 0,
15268                 html : default_text
15269             });
15270             
15271             var o = {};
15272             o[this.valueField] = 0;
15273             o[this.displayField] = default_text;
15274             
15275             this.ios_options.push({
15276                 data : o,
15277                 el : opt
15278             });
15279             
15280         }
15281         
15282         this.store.data.each(function(d, rowIndex){
15283             
15284             var html = '';
15285             
15286             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15287                 html = d.data[this.displayField];
15288             }
15289             
15290             var value = '';
15291             
15292             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15293                 value = d.data[this.valueField];
15294             }
15295             
15296             var option = {
15297                 tag: 'option',
15298                 value : value,
15299                 html : html
15300             };
15301             
15302             if(this.value == d.data[this.valueField]){
15303                 option['selected'] = true;
15304             }
15305             
15306             var opt = this.inputEl().createChild(option);
15307             
15308             this.ios_options.push({
15309                 data : d.data,
15310                 el : opt
15311             });
15312             
15313         }, this);
15314         
15315         this.inputEl().on('change', function(){
15316            this.fireEvent('select', this);
15317         }, this);
15318         
15319     },
15320     
15321     clearIOSView: function()
15322     {
15323         this.inputEl().dom.innerHTML = '';
15324         
15325         this.ios_options = [];
15326     },
15327     
15328     setIOSValue: function(v)
15329     {
15330         this.value = v;
15331         
15332         if(!this.ios_options){
15333             return;
15334         }
15335         
15336         Roo.each(this.ios_options, function(opts){
15337            
15338            opts.el.dom.removeAttribute('selected');
15339            
15340            if(opts.data[this.valueField] != v){
15341                return;
15342            }
15343            
15344            opts.el.dom.setAttribute('selected', true);
15345            
15346         }, this);
15347     }
15348
15349     /** 
15350     * @cfg {Boolean} grow 
15351     * @hide 
15352     */
15353     /** 
15354     * @cfg {Number} growMin 
15355     * @hide 
15356     */
15357     /** 
15358     * @cfg {Number} growMax 
15359     * @hide 
15360     */
15361     /**
15362      * @hide
15363      * @method autoSize
15364      */
15365 });
15366
15367 Roo.apply(Roo.bootstrap.ComboBox,  {
15368     
15369     header : {
15370         tag: 'div',
15371         cls: 'modal-header',
15372         cn: [
15373             {
15374                 tag: 'h4',
15375                 cls: 'modal-title'
15376             }
15377         ]
15378     },
15379     
15380     body : {
15381         tag: 'div',
15382         cls: 'modal-body',
15383         cn: [
15384             {
15385                 tag: 'ul',
15386                 cls: 'list-group'
15387             }
15388         ]
15389     },
15390     
15391     listItemRadio : {
15392         tag: 'li',
15393         cls: 'list-group-item',
15394         cn: [
15395             {
15396                 tag: 'span',
15397                 cls: 'roo-combobox-list-group-item-value'
15398             },
15399             {
15400                 tag: 'div',
15401                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15402                 cn: [
15403                     {
15404                         tag: 'input',
15405                         type: 'radio'
15406                     },
15407                     {
15408                         tag: 'label'
15409                     }
15410                 ]
15411             }
15412         ]
15413     },
15414     
15415     listItemCheckbox : {
15416         tag: 'li',
15417         cls: 'list-group-item',
15418         cn: [
15419             {
15420                 tag: 'span',
15421                 cls: 'roo-combobox-list-group-item-value'
15422             },
15423             {
15424                 tag: 'div',
15425                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15426                 cn: [
15427                     {
15428                         tag: 'input',
15429                         type: 'checkbox'
15430                     },
15431                     {
15432                         tag: 'label'
15433                     }
15434                 ]
15435             }
15436         ]
15437     },
15438     
15439     emptyResult : {
15440         tag: 'div',
15441         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15442     },
15443     
15444     footer : {
15445         tag: 'div',
15446         cls: 'modal-footer',
15447         cn: [
15448             {
15449                 tag: 'div',
15450                 cls: 'row',
15451                 cn: [
15452                     {
15453                         tag: 'div',
15454                         cls: 'col-xs-6 text-left',
15455                         cn: {
15456                             tag: 'button',
15457                             cls: 'btn btn-danger roo-touch-view-cancel',
15458                             html: 'Cancel'
15459                         }
15460                     },
15461                     {
15462                         tag: 'div',
15463                         cls: 'col-xs-6 text-right',
15464                         cn: {
15465                             tag: 'button',
15466                             cls: 'btn btn-success roo-touch-view-ok',
15467                             html: 'OK'
15468                         }
15469                     }
15470                 ]
15471             }
15472         ]
15473         
15474     }
15475 });
15476
15477 Roo.apply(Roo.bootstrap.ComboBox,  {
15478     
15479     touchViewTemplate : {
15480         tag: 'div',
15481         cls: 'modal fade roo-combobox-touch-view',
15482         cn: [
15483             {
15484                 tag: 'div',
15485                 cls: 'modal-dialog',
15486                 style : 'position:fixed', // we have to fix position....
15487                 cn: [
15488                     {
15489                         tag: 'div',
15490                         cls: 'modal-content',
15491                         cn: [
15492                             Roo.bootstrap.ComboBox.header,
15493                             Roo.bootstrap.ComboBox.body,
15494                             Roo.bootstrap.ComboBox.footer
15495                         ]
15496                     }
15497                 ]
15498             }
15499         ]
15500     }
15501 });/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511
15512 /**
15513  * @class Roo.View
15514  * @extends Roo.util.Observable
15515  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15516  * This class also supports single and multi selection modes. <br>
15517  * Create a data model bound view:
15518  <pre><code>
15519  var store = new Roo.data.Store(...);
15520
15521  var view = new Roo.View({
15522     el : "my-element",
15523     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15524  
15525     singleSelect: true,
15526     selectedClass: "ydataview-selected",
15527     store: store
15528  });
15529
15530  // listen for node click?
15531  view.on("click", function(vw, index, node, e){
15532  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15533  });
15534
15535  // load XML data
15536  dataModel.load("foobar.xml");
15537  </code></pre>
15538  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15539  * <br><br>
15540  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15541  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15542  * 
15543  * Note: old style constructor is still suported (container, template, config)
15544  * 
15545  * @constructor
15546  * Create a new View
15547  * @param {Object} config The config object
15548  * 
15549  */
15550 Roo.View = function(config, depreciated_tpl, depreciated_config){
15551     
15552     this.parent = false;
15553     
15554     if (typeof(depreciated_tpl) == 'undefined') {
15555         // new way.. - universal constructor.
15556         Roo.apply(this, config);
15557         this.el  = Roo.get(this.el);
15558     } else {
15559         // old format..
15560         this.el  = Roo.get(config);
15561         this.tpl = depreciated_tpl;
15562         Roo.apply(this, depreciated_config);
15563     }
15564     this.wrapEl  = this.el.wrap().wrap();
15565     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15566     
15567     
15568     if(typeof(this.tpl) == "string"){
15569         this.tpl = new Roo.Template(this.tpl);
15570     } else {
15571         // support xtype ctors..
15572         this.tpl = new Roo.factory(this.tpl, Roo);
15573     }
15574     
15575     
15576     this.tpl.compile();
15577     
15578     /** @private */
15579     this.addEvents({
15580         /**
15581          * @event beforeclick
15582          * Fires before a click is processed. Returns false to cancel the default action.
15583          * @param {Roo.View} this
15584          * @param {Number} index The index of the target node
15585          * @param {HTMLElement} node The target node
15586          * @param {Roo.EventObject} e The raw event object
15587          */
15588             "beforeclick" : true,
15589         /**
15590          * @event click
15591          * Fires when a template node is clicked.
15592          * @param {Roo.View} this
15593          * @param {Number} index The index of the target node
15594          * @param {HTMLElement} node The target node
15595          * @param {Roo.EventObject} e The raw event object
15596          */
15597             "click" : true,
15598         /**
15599          * @event dblclick
15600          * Fires when a template node is double clicked.
15601          * @param {Roo.View} this
15602          * @param {Number} index The index of the target node
15603          * @param {HTMLElement} node The target node
15604          * @param {Roo.EventObject} e The raw event object
15605          */
15606             "dblclick" : true,
15607         /**
15608          * @event contextmenu
15609          * Fires when a template node is right clicked.
15610          * @param {Roo.View} this
15611          * @param {Number} index The index of the target node
15612          * @param {HTMLElement} node The target node
15613          * @param {Roo.EventObject} e The raw event object
15614          */
15615             "contextmenu" : true,
15616         /**
15617          * @event selectionchange
15618          * Fires when the selected nodes change.
15619          * @param {Roo.View} this
15620          * @param {Array} selections Array of the selected nodes
15621          */
15622             "selectionchange" : true,
15623     
15624         /**
15625          * @event beforeselect
15626          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15627          * @param {Roo.View} this
15628          * @param {HTMLElement} node The node to be selected
15629          * @param {Array} selections Array of currently selected nodes
15630          */
15631             "beforeselect" : true,
15632         /**
15633          * @event preparedata
15634          * Fires on every row to render, to allow you to change the data.
15635          * @param {Roo.View} this
15636          * @param {Object} data to be rendered (change this)
15637          */
15638           "preparedata" : true
15639           
15640           
15641         });
15642
15643
15644
15645     this.el.on({
15646         "click": this.onClick,
15647         "dblclick": this.onDblClick,
15648         "contextmenu": this.onContextMenu,
15649         scope:this
15650     });
15651
15652     this.selections = [];
15653     this.nodes = [];
15654     this.cmp = new Roo.CompositeElementLite([]);
15655     if(this.store){
15656         this.store = Roo.factory(this.store, Roo.data);
15657         this.setStore(this.store, true);
15658     }
15659     
15660     if ( this.footer && this.footer.xtype) {
15661            
15662          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15663         
15664         this.footer.dataSource = this.store;
15665         this.footer.container = fctr;
15666         this.footer = Roo.factory(this.footer, Roo);
15667         fctr.insertFirst(this.el);
15668         
15669         // this is a bit insane - as the paging toolbar seems to detach the el..
15670 //        dom.parentNode.parentNode.parentNode
15671          // they get detached?
15672     }
15673     
15674     
15675     Roo.View.superclass.constructor.call(this);
15676     
15677     
15678 };
15679
15680 Roo.extend(Roo.View, Roo.util.Observable, {
15681     
15682      /**
15683      * @cfg {Roo.data.Store} store Data store to load data from.
15684      */
15685     store : false,
15686     
15687     /**
15688      * @cfg {String|Roo.Element} el The container element.
15689      */
15690     el : '',
15691     
15692     /**
15693      * @cfg {String|Roo.Template} tpl The template used by this View 
15694      */
15695     tpl : false,
15696     /**
15697      * @cfg {String} dataName the named area of the template to use as the data area
15698      *                          Works with domtemplates roo-name="name"
15699      */
15700     dataName: false,
15701     /**
15702      * @cfg {String} selectedClass The css class to add to selected nodes
15703      */
15704     selectedClass : "x-view-selected",
15705      /**
15706      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15707      */
15708     emptyText : "",
15709     
15710     /**
15711      * @cfg {String} text to display on mask (default Loading)
15712      */
15713     mask : false,
15714     /**
15715      * @cfg {Boolean} multiSelect Allow multiple selection
15716      */
15717     multiSelect : false,
15718     /**
15719      * @cfg {Boolean} singleSelect Allow single selection
15720      */
15721     singleSelect:  false,
15722     
15723     /**
15724      * @cfg {Boolean} toggleSelect - selecting 
15725      */
15726     toggleSelect : false,
15727     
15728     /**
15729      * @cfg {Boolean} tickable - selecting 
15730      */
15731     tickable : false,
15732     
15733     /**
15734      * Returns the element this view is bound to.
15735      * @return {Roo.Element}
15736      */
15737     getEl : function(){
15738         return this.wrapEl;
15739     },
15740     
15741     
15742
15743     /**
15744      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15745      */
15746     refresh : function(){
15747         //Roo.log('refresh');
15748         var t = this.tpl;
15749         
15750         // if we are using something like 'domtemplate', then
15751         // the what gets used is:
15752         // t.applySubtemplate(NAME, data, wrapping data..)
15753         // the outer template then get' applied with
15754         //     the store 'extra data'
15755         // and the body get's added to the
15756         //      roo-name="data" node?
15757         //      <span class='roo-tpl-{name}'></span> ?????
15758         
15759         
15760         
15761         this.clearSelections();
15762         this.el.update("");
15763         var html = [];
15764         var records = this.store.getRange();
15765         if(records.length < 1) {
15766             
15767             // is this valid??  = should it render a template??
15768             
15769             this.el.update(this.emptyText);
15770             return;
15771         }
15772         var el = this.el;
15773         if (this.dataName) {
15774             this.el.update(t.apply(this.store.meta)); //????
15775             el = this.el.child('.roo-tpl-' + this.dataName);
15776         }
15777         
15778         for(var i = 0, len = records.length; i < len; i++){
15779             var data = this.prepareData(records[i].data, i, records[i]);
15780             this.fireEvent("preparedata", this, data, i, records[i]);
15781             
15782             var d = Roo.apply({}, data);
15783             
15784             if(this.tickable){
15785                 Roo.apply(d, {'roo-id' : Roo.id()});
15786                 
15787                 var _this = this;
15788             
15789                 Roo.each(this.parent.item, function(item){
15790                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15791                         return;
15792                     }
15793                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15794                 });
15795             }
15796             
15797             html[html.length] = Roo.util.Format.trim(
15798                 this.dataName ?
15799                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15800                     t.apply(d)
15801             );
15802         }
15803         
15804         
15805         
15806         el.update(html.join(""));
15807         this.nodes = el.dom.childNodes;
15808         this.updateIndexes(0);
15809     },
15810     
15811
15812     /**
15813      * Function to override to reformat the data that is sent to
15814      * the template for each node.
15815      * DEPRICATED - use the preparedata event handler.
15816      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15817      * a JSON object for an UpdateManager bound view).
15818      */
15819     prepareData : function(data, index, record)
15820     {
15821         this.fireEvent("preparedata", this, data, index, record);
15822         return data;
15823     },
15824
15825     onUpdate : function(ds, record){
15826         // Roo.log('on update');   
15827         this.clearSelections();
15828         var index = this.store.indexOf(record);
15829         var n = this.nodes[index];
15830         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15831         n.parentNode.removeChild(n);
15832         this.updateIndexes(index, index);
15833     },
15834
15835     
15836     
15837 // --------- FIXME     
15838     onAdd : function(ds, records, index)
15839     {
15840         //Roo.log(['on Add', ds, records, index] );        
15841         this.clearSelections();
15842         if(this.nodes.length == 0){
15843             this.refresh();
15844             return;
15845         }
15846         var n = this.nodes[index];
15847         for(var i = 0, len = records.length; i < len; i++){
15848             var d = this.prepareData(records[i].data, i, records[i]);
15849             if(n){
15850                 this.tpl.insertBefore(n, d);
15851             }else{
15852                 
15853                 this.tpl.append(this.el, d);
15854             }
15855         }
15856         this.updateIndexes(index);
15857     },
15858
15859     onRemove : function(ds, record, index){
15860        // Roo.log('onRemove');
15861         this.clearSelections();
15862         var el = this.dataName  ?
15863             this.el.child('.roo-tpl-' + this.dataName) :
15864             this.el; 
15865         
15866         el.dom.removeChild(this.nodes[index]);
15867         this.updateIndexes(index);
15868     },
15869
15870     /**
15871      * Refresh an individual node.
15872      * @param {Number} index
15873      */
15874     refreshNode : function(index){
15875         this.onUpdate(this.store, this.store.getAt(index));
15876     },
15877
15878     updateIndexes : function(startIndex, endIndex){
15879         var ns = this.nodes;
15880         startIndex = startIndex || 0;
15881         endIndex = endIndex || ns.length - 1;
15882         for(var i = startIndex; i <= endIndex; i++){
15883             ns[i].nodeIndex = i;
15884         }
15885     },
15886
15887     /**
15888      * Changes the data store this view uses and refresh the view.
15889      * @param {Store} store
15890      */
15891     setStore : function(store, initial){
15892         if(!initial && this.store){
15893             this.store.un("datachanged", this.refresh);
15894             this.store.un("add", this.onAdd);
15895             this.store.un("remove", this.onRemove);
15896             this.store.un("update", this.onUpdate);
15897             this.store.un("clear", this.refresh);
15898             this.store.un("beforeload", this.onBeforeLoad);
15899             this.store.un("load", this.onLoad);
15900             this.store.un("loadexception", this.onLoad);
15901         }
15902         if(store){
15903           
15904             store.on("datachanged", this.refresh, this);
15905             store.on("add", this.onAdd, this);
15906             store.on("remove", this.onRemove, this);
15907             store.on("update", this.onUpdate, this);
15908             store.on("clear", this.refresh, this);
15909             store.on("beforeload", this.onBeforeLoad, this);
15910             store.on("load", this.onLoad, this);
15911             store.on("loadexception", this.onLoad, this);
15912         }
15913         
15914         if(store){
15915             this.refresh();
15916         }
15917     },
15918     /**
15919      * onbeforeLoad - masks the loading area.
15920      *
15921      */
15922     onBeforeLoad : function(store,opts)
15923     {
15924          //Roo.log('onBeforeLoad');   
15925         if (!opts.add) {
15926             this.el.update("");
15927         }
15928         this.el.mask(this.mask ? this.mask : "Loading" ); 
15929     },
15930     onLoad : function ()
15931     {
15932         this.el.unmask();
15933     },
15934     
15935
15936     /**
15937      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15938      * @param {HTMLElement} node
15939      * @return {HTMLElement} The template node
15940      */
15941     findItemFromChild : function(node){
15942         var el = this.dataName  ?
15943             this.el.child('.roo-tpl-' + this.dataName,true) :
15944             this.el.dom; 
15945         
15946         if(!node || node.parentNode == el){
15947                     return node;
15948             }
15949             var p = node.parentNode;
15950             while(p && p != el){
15951             if(p.parentNode == el){
15952                 return p;
15953             }
15954             p = p.parentNode;
15955         }
15956             return null;
15957     },
15958
15959     /** @ignore */
15960     onClick : function(e){
15961         var item = this.findItemFromChild(e.getTarget());
15962         if(item){
15963             var index = this.indexOf(item);
15964             if(this.onItemClick(item, index, e) !== false){
15965                 this.fireEvent("click", this, index, item, e);
15966             }
15967         }else{
15968             this.clearSelections();
15969         }
15970     },
15971
15972     /** @ignore */
15973     onContextMenu : function(e){
15974         var item = this.findItemFromChild(e.getTarget());
15975         if(item){
15976             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15977         }
15978     },
15979
15980     /** @ignore */
15981     onDblClick : function(e){
15982         var item = this.findItemFromChild(e.getTarget());
15983         if(item){
15984             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15985         }
15986     },
15987
15988     onItemClick : function(item, index, e)
15989     {
15990         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15991             return false;
15992         }
15993         if (this.toggleSelect) {
15994             var m = this.isSelected(item) ? 'unselect' : 'select';
15995             //Roo.log(m);
15996             var _t = this;
15997             _t[m](item, true, false);
15998             return true;
15999         }
16000         if(this.multiSelect || this.singleSelect){
16001             if(this.multiSelect && e.shiftKey && this.lastSelection){
16002                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16003             }else{
16004                 this.select(item, this.multiSelect && e.ctrlKey);
16005                 this.lastSelection = item;
16006             }
16007             
16008             if(!this.tickable){
16009                 e.preventDefault();
16010             }
16011             
16012         }
16013         return true;
16014     },
16015
16016     /**
16017      * Get the number of selected nodes.
16018      * @return {Number}
16019      */
16020     getSelectionCount : function(){
16021         return this.selections.length;
16022     },
16023
16024     /**
16025      * Get the currently selected nodes.
16026      * @return {Array} An array of HTMLElements
16027      */
16028     getSelectedNodes : function(){
16029         return this.selections;
16030     },
16031
16032     /**
16033      * Get the indexes of the selected nodes.
16034      * @return {Array}
16035      */
16036     getSelectedIndexes : function(){
16037         var indexes = [], s = this.selections;
16038         for(var i = 0, len = s.length; i < len; i++){
16039             indexes.push(s[i].nodeIndex);
16040         }
16041         return indexes;
16042     },
16043
16044     /**
16045      * Clear all selections
16046      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16047      */
16048     clearSelections : function(suppressEvent){
16049         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16050             this.cmp.elements = this.selections;
16051             this.cmp.removeClass(this.selectedClass);
16052             this.selections = [];
16053             if(!suppressEvent){
16054                 this.fireEvent("selectionchange", this, this.selections);
16055             }
16056         }
16057     },
16058
16059     /**
16060      * Returns true if the passed node is selected
16061      * @param {HTMLElement/Number} node The node or node index
16062      * @return {Boolean}
16063      */
16064     isSelected : function(node){
16065         var s = this.selections;
16066         if(s.length < 1){
16067             return false;
16068         }
16069         node = this.getNode(node);
16070         return s.indexOf(node) !== -1;
16071     },
16072
16073     /**
16074      * Selects nodes.
16075      * @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
16076      * @param {Boolean} keepExisting (optional) true to keep existing selections
16077      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16078      */
16079     select : function(nodeInfo, keepExisting, suppressEvent){
16080         if(nodeInfo instanceof Array){
16081             if(!keepExisting){
16082                 this.clearSelections(true);
16083             }
16084             for(var i = 0, len = nodeInfo.length; i < len; i++){
16085                 this.select(nodeInfo[i], true, true);
16086             }
16087             return;
16088         } 
16089         var node = this.getNode(nodeInfo);
16090         if(!node || this.isSelected(node)){
16091             return; // already selected.
16092         }
16093         if(!keepExisting){
16094             this.clearSelections(true);
16095         }
16096         
16097         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16098             Roo.fly(node).addClass(this.selectedClass);
16099             this.selections.push(node);
16100             if(!suppressEvent){
16101                 this.fireEvent("selectionchange", this, this.selections);
16102             }
16103         }
16104         
16105         
16106     },
16107       /**
16108      * Unselects nodes.
16109      * @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
16110      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16111      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16112      */
16113     unselect : function(nodeInfo, keepExisting, suppressEvent)
16114     {
16115         if(nodeInfo instanceof Array){
16116             Roo.each(this.selections, function(s) {
16117                 this.unselect(s, nodeInfo);
16118             }, this);
16119             return;
16120         }
16121         var node = this.getNode(nodeInfo);
16122         if(!node || !this.isSelected(node)){
16123             //Roo.log("not selected");
16124             return; // not selected.
16125         }
16126         // fireevent???
16127         var ns = [];
16128         Roo.each(this.selections, function(s) {
16129             if (s == node ) {
16130                 Roo.fly(node).removeClass(this.selectedClass);
16131
16132                 return;
16133             }
16134             ns.push(s);
16135         },this);
16136         
16137         this.selections= ns;
16138         this.fireEvent("selectionchange", this, this.selections);
16139     },
16140
16141     /**
16142      * Gets a template node.
16143      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16144      * @return {HTMLElement} The node or null if it wasn't found
16145      */
16146     getNode : function(nodeInfo){
16147         if(typeof nodeInfo == "string"){
16148             return document.getElementById(nodeInfo);
16149         }else if(typeof nodeInfo == "number"){
16150             return this.nodes[nodeInfo];
16151         }
16152         return nodeInfo;
16153     },
16154
16155     /**
16156      * Gets a range template nodes.
16157      * @param {Number} startIndex
16158      * @param {Number} endIndex
16159      * @return {Array} An array of nodes
16160      */
16161     getNodes : function(start, end){
16162         var ns = this.nodes;
16163         start = start || 0;
16164         end = typeof end == "undefined" ? ns.length - 1 : end;
16165         var nodes = [];
16166         if(start <= end){
16167             for(var i = start; i <= end; i++){
16168                 nodes.push(ns[i]);
16169             }
16170         } else{
16171             for(var i = start; i >= end; i--){
16172                 nodes.push(ns[i]);
16173             }
16174         }
16175         return nodes;
16176     },
16177
16178     /**
16179      * Finds the index of the passed node
16180      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16181      * @return {Number} The index of the node or -1
16182      */
16183     indexOf : function(node){
16184         node = this.getNode(node);
16185         if(typeof node.nodeIndex == "number"){
16186             return node.nodeIndex;
16187         }
16188         var ns = this.nodes;
16189         for(var i = 0, len = ns.length; i < len; i++){
16190             if(ns[i] == node){
16191                 return i;
16192             }
16193         }
16194         return -1;
16195     }
16196 });
16197 /*
16198  * - LGPL
16199  *
16200  * based on jquery fullcalendar
16201  * 
16202  */
16203
16204 Roo.bootstrap = Roo.bootstrap || {};
16205 /**
16206  * @class Roo.bootstrap.Calendar
16207  * @extends Roo.bootstrap.Component
16208  * Bootstrap Calendar class
16209  * @cfg {Boolean} loadMask (true|false) default false
16210  * @cfg {Object} header generate the user specific header of the calendar, default false
16211
16212  * @constructor
16213  * Create a new Container
16214  * @param {Object} config The config object
16215  */
16216
16217
16218
16219 Roo.bootstrap.Calendar = function(config){
16220     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16221      this.addEvents({
16222         /**
16223              * @event select
16224              * Fires when a date is selected
16225              * @param {DatePicker} this
16226              * @param {Date} date The selected date
16227              */
16228         'select': true,
16229         /**
16230              * @event monthchange
16231              * Fires when the displayed month changes 
16232              * @param {DatePicker} this
16233              * @param {Date} date The selected month
16234              */
16235         'monthchange': true,
16236         /**
16237              * @event evententer
16238              * Fires when mouse over an event
16239              * @param {Calendar} this
16240              * @param {event} Event
16241              */
16242         'evententer': true,
16243         /**
16244              * @event eventleave
16245              * Fires when the mouse leaves an
16246              * @param {Calendar} this
16247              * @param {event}
16248              */
16249         'eventleave': true,
16250         /**
16251              * @event eventclick
16252              * Fires when the mouse click an
16253              * @param {Calendar} this
16254              * @param {event}
16255              */
16256         'eventclick': true
16257         
16258     });
16259
16260 };
16261
16262 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16263     
16264      /**
16265      * @cfg {Number} startDay
16266      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16267      */
16268     startDay : 0,
16269     
16270     loadMask : false,
16271     
16272     header : false,
16273       
16274     getAutoCreate : function(){
16275         
16276         
16277         var fc_button = function(name, corner, style, content ) {
16278             return Roo.apply({},{
16279                 tag : 'span',
16280                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16281                          (corner.length ?
16282                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16283                             ''
16284                         ),
16285                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16286                 unselectable: 'on'
16287             });
16288         };
16289         
16290         var header = {};
16291         
16292         if(!this.header){
16293             header = {
16294                 tag : 'table',
16295                 cls : 'fc-header',
16296                 style : 'width:100%',
16297                 cn : [
16298                     {
16299                         tag: 'tr',
16300                         cn : [
16301                             {
16302                                 tag : 'td',
16303                                 cls : 'fc-header-left',
16304                                 cn : [
16305                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16306                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16307                                     { tag: 'span', cls: 'fc-header-space' },
16308                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16309
16310
16311                                 ]
16312                             },
16313
16314                             {
16315                                 tag : 'td',
16316                                 cls : 'fc-header-center',
16317                                 cn : [
16318                                     {
16319                                         tag: 'span',
16320                                         cls: 'fc-header-title',
16321                                         cn : {
16322                                             tag: 'H2',
16323                                             html : 'month / year'
16324                                         }
16325                                     }
16326
16327                                 ]
16328                             },
16329                             {
16330                                 tag : 'td',
16331                                 cls : 'fc-header-right',
16332                                 cn : [
16333                               /*      fc_button('month', 'left', '', 'month' ),
16334                                     fc_button('week', '', '', 'week' ),
16335                                     fc_button('day', 'right', '', 'day' )
16336                                 */    
16337
16338                                 ]
16339                             }
16340
16341                         ]
16342                     }
16343                 ]
16344             };
16345         }
16346         
16347         header = this.header;
16348         
16349        
16350         var cal_heads = function() {
16351             var ret = [];
16352             // fixme - handle this.
16353             
16354             for (var i =0; i < Date.dayNames.length; i++) {
16355                 var d = Date.dayNames[i];
16356                 ret.push({
16357                     tag: 'th',
16358                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16359                     html : d.substring(0,3)
16360                 });
16361                 
16362             }
16363             ret[0].cls += ' fc-first';
16364             ret[6].cls += ' fc-last';
16365             return ret;
16366         };
16367         var cal_cell = function(n) {
16368             return  {
16369                 tag: 'td',
16370                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16371                 cn : [
16372                     {
16373                         cn : [
16374                             {
16375                                 cls: 'fc-day-number',
16376                                 html: 'D'
16377                             },
16378                             {
16379                                 cls: 'fc-day-content',
16380                              
16381                                 cn : [
16382                                      {
16383                                         style: 'position: relative;' // height: 17px;
16384                                     }
16385                                 ]
16386                             }
16387                             
16388                             
16389                         ]
16390                     }
16391                 ]
16392                 
16393             }
16394         };
16395         var cal_rows = function() {
16396             
16397             var ret = [];
16398             for (var r = 0; r < 6; r++) {
16399                 var row= {
16400                     tag : 'tr',
16401                     cls : 'fc-week',
16402                     cn : []
16403                 };
16404                 
16405                 for (var i =0; i < Date.dayNames.length; i++) {
16406                     var d = Date.dayNames[i];
16407                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16408
16409                 }
16410                 row.cn[0].cls+=' fc-first';
16411                 row.cn[0].cn[0].style = 'min-height:90px';
16412                 row.cn[6].cls+=' fc-last';
16413                 ret.push(row);
16414                 
16415             }
16416             ret[0].cls += ' fc-first';
16417             ret[4].cls += ' fc-prev-last';
16418             ret[5].cls += ' fc-last';
16419             return ret;
16420             
16421         };
16422         
16423         var cal_table = {
16424             tag: 'table',
16425             cls: 'fc-border-separate',
16426             style : 'width:100%',
16427             cellspacing  : 0,
16428             cn : [
16429                 { 
16430                     tag: 'thead',
16431                     cn : [
16432                         { 
16433                             tag: 'tr',
16434                             cls : 'fc-first fc-last',
16435                             cn : cal_heads()
16436                         }
16437                     ]
16438                 },
16439                 { 
16440                     tag: 'tbody',
16441                     cn : cal_rows()
16442                 }
16443                   
16444             ]
16445         };
16446          
16447          var cfg = {
16448             cls : 'fc fc-ltr',
16449             cn : [
16450                 header,
16451                 {
16452                     cls : 'fc-content',
16453                     style : "position: relative;",
16454                     cn : [
16455                         {
16456                             cls : 'fc-view fc-view-month fc-grid',
16457                             style : 'position: relative',
16458                             unselectable : 'on',
16459                             cn : [
16460                                 {
16461                                     cls : 'fc-event-container',
16462                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16463                                 },
16464                                 cal_table
16465                             ]
16466                         }
16467                     ]
16468     
16469                 }
16470            ] 
16471             
16472         };
16473         
16474          
16475         
16476         return cfg;
16477     },
16478     
16479     
16480     initEvents : function()
16481     {
16482         if(!this.store){
16483             throw "can not find store for calendar";
16484         }
16485         
16486         var mark = {
16487             tag: "div",
16488             cls:"x-dlg-mask",
16489             style: "text-align:center",
16490             cn: [
16491                 {
16492                     tag: "div",
16493                     style: "background-color:white;width:50%;margin:250 auto",
16494                     cn: [
16495                         {
16496                             tag: "img",
16497                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16498                         },
16499                         {
16500                             tag: "span",
16501                             html: "Loading"
16502                         }
16503                         
16504                     ]
16505                 }
16506             ]
16507         };
16508         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16509         
16510         var size = this.el.select('.fc-content', true).first().getSize();
16511         this.maskEl.setSize(size.width, size.height);
16512         this.maskEl.enableDisplayMode("block");
16513         if(!this.loadMask){
16514             this.maskEl.hide();
16515         }
16516         
16517         this.store = Roo.factory(this.store, Roo.data);
16518         this.store.on('load', this.onLoad, this);
16519         this.store.on('beforeload', this.onBeforeLoad, this);
16520         
16521         this.resize();
16522         
16523         this.cells = this.el.select('.fc-day',true);
16524         //Roo.log(this.cells);
16525         this.textNodes = this.el.query('.fc-day-number');
16526         this.cells.addClassOnOver('fc-state-hover');
16527         
16528         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16529         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16530         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16531         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16532         
16533         this.on('monthchange', this.onMonthChange, this);
16534         
16535         this.update(new Date().clearTime());
16536     },
16537     
16538     resize : function() {
16539         var sz  = this.el.getSize();
16540         
16541         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16542         this.el.select('.fc-day-content div',true).setHeight(34);
16543     },
16544     
16545     
16546     // private
16547     showPrevMonth : function(e){
16548         this.update(this.activeDate.add("mo", -1));
16549     },
16550     showToday : function(e){
16551         this.update(new Date().clearTime());
16552     },
16553     // private
16554     showNextMonth : function(e){
16555         this.update(this.activeDate.add("mo", 1));
16556     },
16557
16558     // private
16559     showPrevYear : function(){
16560         this.update(this.activeDate.add("y", -1));
16561     },
16562
16563     // private
16564     showNextYear : function(){
16565         this.update(this.activeDate.add("y", 1));
16566     },
16567
16568     
16569    // private
16570     update : function(date)
16571     {
16572         var vd = this.activeDate;
16573         this.activeDate = date;
16574 //        if(vd && this.el){
16575 //            var t = date.getTime();
16576 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16577 //                Roo.log('using add remove');
16578 //                
16579 //                this.fireEvent('monthchange', this, date);
16580 //                
16581 //                this.cells.removeClass("fc-state-highlight");
16582 //                this.cells.each(function(c){
16583 //                   if(c.dateValue == t){
16584 //                       c.addClass("fc-state-highlight");
16585 //                       setTimeout(function(){
16586 //                            try{c.dom.firstChild.focus();}catch(e){}
16587 //                       }, 50);
16588 //                       return false;
16589 //                   }
16590 //                   return true;
16591 //                });
16592 //                return;
16593 //            }
16594 //        }
16595         
16596         var days = date.getDaysInMonth();
16597         
16598         var firstOfMonth = date.getFirstDateOfMonth();
16599         var startingPos = firstOfMonth.getDay()-this.startDay;
16600         
16601         if(startingPos < this.startDay){
16602             startingPos += 7;
16603         }
16604         
16605         var pm = date.add(Date.MONTH, -1);
16606         var prevStart = pm.getDaysInMonth()-startingPos;
16607 //        
16608         this.cells = this.el.select('.fc-day',true);
16609         this.textNodes = this.el.query('.fc-day-number');
16610         this.cells.addClassOnOver('fc-state-hover');
16611         
16612         var cells = this.cells.elements;
16613         var textEls = this.textNodes;
16614         
16615         Roo.each(cells, function(cell){
16616             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16617         });
16618         
16619         days += startingPos;
16620
16621         // convert everything to numbers so it's fast
16622         var day = 86400000;
16623         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16624         //Roo.log(d);
16625         //Roo.log(pm);
16626         //Roo.log(prevStart);
16627         
16628         var today = new Date().clearTime().getTime();
16629         var sel = date.clearTime().getTime();
16630         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16631         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16632         var ddMatch = this.disabledDatesRE;
16633         var ddText = this.disabledDatesText;
16634         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16635         var ddaysText = this.disabledDaysText;
16636         var format = this.format;
16637         
16638         var setCellClass = function(cal, cell){
16639             cell.row = 0;
16640             cell.events = [];
16641             cell.more = [];
16642             //Roo.log('set Cell Class');
16643             cell.title = "";
16644             var t = d.getTime();
16645             
16646             //Roo.log(d);
16647             
16648             cell.dateValue = t;
16649             if(t == today){
16650                 cell.className += " fc-today";
16651                 cell.className += " fc-state-highlight";
16652                 cell.title = cal.todayText;
16653             }
16654             if(t == sel){
16655                 // disable highlight in other month..
16656                 //cell.className += " fc-state-highlight";
16657                 
16658             }
16659             // disabling
16660             if(t < min) {
16661                 cell.className = " fc-state-disabled";
16662                 cell.title = cal.minText;
16663                 return;
16664             }
16665             if(t > max) {
16666                 cell.className = " fc-state-disabled";
16667                 cell.title = cal.maxText;
16668                 return;
16669             }
16670             if(ddays){
16671                 if(ddays.indexOf(d.getDay()) != -1){
16672                     cell.title = ddaysText;
16673                     cell.className = " fc-state-disabled";
16674                 }
16675             }
16676             if(ddMatch && format){
16677                 var fvalue = d.dateFormat(format);
16678                 if(ddMatch.test(fvalue)){
16679                     cell.title = ddText.replace("%0", fvalue);
16680                     cell.className = " fc-state-disabled";
16681                 }
16682             }
16683             
16684             if (!cell.initialClassName) {
16685                 cell.initialClassName = cell.dom.className;
16686             }
16687             
16688             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16689         };
16690
16691         var i = 0;
16692         
16693         for(; i < startingPos; i++) {
16694             textEls[i].innerHTML = (++prevStart);
16695             d.setDate(d.getDate()+1);
16696             
16697             cells[i].className = "fc-past fc-other-month";
16698             setCellClass(this, cells[i]);
16699         }
16700         
16701         var intDay = 0;
16702         
16703         for(; i < days; i++){
16704             intDay = i - startingPos + 1;
16705             textEls[i].innerHTML = (intDay);
16706             d.setDate(d.getDate()+1);
16707             
16708             cells[i].className = ''; // "x-date-active";
16709             setCellClass(this, cells[i]);
16710         }
16711         var extraDays = 0;
16712         
16713         for(; i < 42; i++) {
16714             textEls[i].innerHTML = (++extraDays);
16715             d.setDate(d.getDate()+1);
16716             
16717             cells[i].className = "fc-future fc-other-month";
16718             setCellClass(this, cells[i]);
16719         }
16720         
16721         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16722         
16723         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16724         
16725         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16726         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16727         
16728         if(totalRows != 6){
16729             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16730             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16731         }
16732         
16733         this.fireEvent('monthchange', this, date);
16734         
16735         
16736         /*
16737         if(!this.internalRender){
16738             var main = this.el.dom.firstChild;
16739             var w = main.offsetWidth;
16740             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16741             Roo.fly(main).setWidth(w);
16742             this.internalRender = true;
16743             // opera does not respect the auto grow header center column
16744             // then, after it gets a width opera refuses to recalculate
16745             // without a second pass
16746             if(Roo.isOpera && !this.secondPass){
16747                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16748                 this.secondPass = true;
16749                 this.update.defer(10, this, [date]);
16750             }
16751         }
16752         */
16753         
16754     },
16755     
16756     findCell : function(dt) {
16757         dt = dt.clearTime().getTime();
16758         var ret = false;
16759         this.cells.each(function(c){
16760             //Roo.log("check " +c.dateValue + '?=' + dt);
16761             if(c.dateValue == dt){
16762                 ret = c;
16763                 return false;
16764             }
16765             return true;
16766         });
16767         
16768         return ret;
16769     },
16770     
16771     findCells : function(ev) {
16772         var s = ev.start.clone().clearTime().getTime();
16773        // Roo.log(s);
16774         var e= ev.end.clone().clearTime().getTime();
16775        // Roo.log(e);
16776         var ret = [];
16777         this.cells.each(function(c){
16778              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16779             
16780             if(c.dateValue > e){
16781                 return ;
16782             }
16783             if(c.dateValue < s){
16784                 return ;
16785             }
16786             ret.push(c);
16787         });
16788         
16789         return ret;    
16790     },
16791     
16792 //    findBestRow: function(cells)
16793 //    {
16794 //        var ret = 0;
16795 //        
16796 //        for (var i =0 ; i < cells.length;i++) {
16797 //            ret  = Math.max(cells[i].rows || 0,ret);
16798 //        }
16799 //        return ret;
16800 //        
16801 //    },
16802     
16803     
16804     addItem : function(ev)
16805     {
16806         // look for vertical location slot in
16807         var cells = this.findCells(ev);
16808         
16809 //        ev.row = this.findBestRow(cells);
16810         
16811         // work out the location.
16812         
16813         var crow = false;
16814         var rows = [];
16815         for(var i =0; i < cells.length; i++) {
16816             
16817             cells[i].row = cells[0].row;
16818             
16819             if(i == 0){
16820                 cells[i].row = cells[i].row + 1;
16821             }
16822             
16823             if (!crow) {
16824                 crow = {
16825                     start : cells[i],
16826                     end :  cells[i]
16827                 };
16828                 continue;
16829             }
16830             if (crow.start.getY() == cells[i].getY()) {
16831                 // on same row.
16832                 crow.end = cells[i];
16833                 continue;
16834             }
16835             // different row.
16836             rows.push(crow);
16837             crow = {
16838                 start: cells[i],
16839                 end : cells[i]
16840             };
16841             
16842         }
16843         
16844         rows.push(crow);
16845         ev.els = [];
16846         ev.rows = rows;
16847         ev.cells = cells;
16848         
16849         cells[0].events.push(ev);
16850         
16851         this.calevents.push(ev);
16852     },
16853     
16854     clearEvents: function() {
16855         
16856         if(!this.calevents){
16857             return;
16858         }
16859         
16860         Roo.each(this.cells.elements, function(c){
16861             c.row = 0;
16862             c.events = [];
16863             c.more = [];
16864         });
16865         
16866         Roo.each(this.calevents, function(e) {
16867             Roo.each(e.els, function(el) {
16868                 el.un('mouseenter' ,this.onEventEnter, this);
16869                 el.un('mouseleave' ,this.onEventLeave, this);
16870                 el.remove();
16871             },this);
16872         },this);
16873         
16874         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16875             e.remove();
16876         });
16877         
16878     },
16879     
16880     renderEvents: function()
16881     {   
16882         var _this = this;
16883         
16884         this.cells.each(function(c) {
16885             
16886             if(c.row < 5){
16887                 return;
16888             }
16889             
16890             var ev = c.events;
16891             
16892             var r = 4;
16893             if(c.row != c.events.length){
16894                 r = 4 - (4 - (c.row - c.events.length));
16895             }
16896             
16897             c.events = ev.slice(0, r);
16898             c.more = ev.slice(r);
16899             
16900             if(c.more.length && c.more.length == 1){
16901                 c.events.push(c.more.pop());
16902             }
16903             
16904             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16905             
16906         });
16907             
16908         this.cells.each(function(c) {
16909             
16910             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16911             
16912             
16913             for (var e = 0; e < c.events.length; e++){
16914                 var ev = c.events[e];
16915                 var rows = ev.rows;
16916                 
16917                 for(var i = 0; i < rows.length; i++) {
16918                 
16919                     // how many rows should it span..
16920
16921                     var  cfg = {
16922                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16923                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16924
16925                         unselectable : "on",
16926                         cn : [
16927                             {
16928                                 cls: 'fc-event-inner',
16929                                 cn : [
16930     //                                {
16931     //                                  tag:'span',
16932     //                                  cls: 'fc-event-time',
16933     //                                  html : cells.length > 1 ? '' : ev.time
16934     //                                },
16935                                     {
16936                                       tag:'span',
16937                                       cls: 'fc-event-title',
16938                                       html : String.format('{0}', ev.title)
16939                                     }
16940
16941
16942                                 ]
16943                             },
16944                             {
16945                                 cls: 'ui-resizable-handle ui-resizable-e',
16946                                 html : '&nbsp;&nbsp;&nbsp'
16947                             }
16948
16949                         ]
16950                     };
16951
16952                     if (i == 0) {
16953                         cfg.cls += ' fc-event-start';
16954                     }
16955                     if ((i+1) == rows.length) {
16956                         cfg.cls += ' fc-event-end';
16957                     }
16958
16959                     var ctr = _this.el.select('.fc-event-container',true).first();
16960                     var cg = ctr.createChild(cfg);
16961
16962                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16963                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16964
16965                     var r = (c.more.length) ? 1 : 0;
16966                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16967                     cg.setWidth(ebox.right - sbox.x -2);
16968
16969                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16970                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16971                     cg.on('click', _this.onEventClick, _this, ev);
16972
16973                     ev.els.push(cg);
16974                     
16975                 }
16976                 
16977             }
16978             
16979             
16980             if(c.more.length){
16981                 var  cfg = {
16982                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16983                     style : 'position: absolute',
16984                     unselectable : "on",
16985                     cn : [
16986                         {
16987                             cls: 'fc-event-inner',
16988                             cn : [
16989                                 {
16990                                   tag:'span',
16991                                   cls: 'fc-event-title',
16992                                   html : 'More'
16993                                 }
16994
16995
16996                             ]
16997                         },
16998                         {
16999                             cls: 'ui-resizable-handle ui-resizable-e',
17000                             html : '&nbsp;&nbsp;&nbsp'
17001                         }
17002
17003                     ]
17004                 };
17005
17006                 var ctr = _this.el.select('.fc-event-container',true).first();
17007                 var cg = ctr.createChild(cfg);
17008
17009                 var sbox = c.select('.fc-day-content',true).first().getBox();
17010                 var ebox = c.select('.fc-day-content',true).first().getBox();
17011                 //Roo.log(cg);
17012                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17013                 cg.setWidth(ebox.right - sbox.x -2);
17014
17015                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17016                 
17017             }
17018             
17019         });
17020         
17021         
17022         
17023     },
17024     
17025     onEventEnter: function (e, el,event,d) {
17026         this.fireEvent('evententer', this, el, event);
17027     },
17028     
17029     onEventLeave: function (e, el,event,d) {
17030         this.fireEvent('eventleave', this, el, event);
17031     },
17032     
17033     onEventClick: function (e, el,event,d) {
17034         this.fireEvent('eventclick', this, el, event);
17035     },
17036     
17037     onMonthChange: function () {
17038         this.store.load();
17039     },
17040     
17041     onMoreEventClick: function(e, el, more)
17042     {
17043         var _this = this;
17044         
17045         this.calpopover.placement = 'right';
17046         this.calpopover.setTitle('More');
17047         
17048         this.calpopover.setContent('');
17049         
17050         var ctr = this.calpopover.el.select('.popover-content', true).first();
17051         
17052         Roo.each(more, function(m){
17053             var cfg = {
17054                 cls : 'fc-event-hori fc-event-draggable',
17055                 html : m.title
17056             };
17057             var cg = ctr.createChild(cfg);
17058             
17059             cg.on('click', _this.onEventClick, _this, m);
17060         });
17061         
17062         this.calpopover.show(el);
17063         
17064         
17065     },
17066     
17067     onLoad: function () 
17068     {   
17069         this.calevents = [];
17070         var cal = this;
17071         
17072         if(this.store.getCount() > 0){
17073             this.store.data.each(function(d){
17074                cal.addItem({
17075                     id : d.data.id,
17076                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17077                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17078                     time : d.data.start_time,
17079                     title : d.data.title,
17080                     description : d.data.description,
17081                     venue : d.data.venue
17082                 });
17083             });
17084         }
17085         
17086         this.renderEvents();
17087         
17088         if(this.calevents.length && this.loadMask){
17089             this.maskEl.hide();
17090         }
17091     },
17092     
17093     onBeforeLoad: function()
17094     {
17095         this.clearEvents();
17096         if(this.loadMask){
17097             this.maskEl.show();
17098         }
17099     }
17100 });
17101
17102  
17103  /*
17104  * - LGPL
17105  *
17106  * element
17107  * 
17108  */
17109
17110 /**
17111  * @class Roo.bootstrap.Popover
17112  * @extends Roo.bootstrap.Component
17113  * Bootstrap Popover class
17114  * @cfg {String} html contents of the popover   (or false to use children..)
17115  * @cfg {String} title of popover (or false to hide)
17116  * @cfg {String} placement how it is placed
17117  * @cfg {String} trigger click || hover (or false to trigger manually)
17118  * @cfg {String} over what (parent or false to trigger manually.)
17119  * @cfg {Number} delay - delay before showing
17120  
17121  * @constructor
17122  * Create a new Popover
17123  * @param {Object} config The config object
17124  */
17125
17126 Roo.bootstrap.Popover = function(config){
17127     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17128     
17129     this.addEvents({
17130         // raw events
17131          /**
17132          * @event show
17133          * After the popover show
17134          * 
17135          * @param {Roo.bootstrap.Popover} this
17136          */
17137         "show" : true,
17138         /**
17139          * @event hide
17140          * After the popover hide
17141          * 
17142          * @param {Roo.bootstrap.Popover} this
17143          */
17144         "hide" : true
17145     });
17146 };
17147
17148 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17149     
17150     title: 'Fill in a title',
17151     html: false,
17152     
17153     placement : 'right',
17154     trigger : 'hover', // hover
17155     
17156     delay : 0,
17157     
17158     over: 'parent',
17159     
17160     can_build_overlaid : false,
17161     
17162     getChildContainer : function()
17163     {
17164         return this.el.select('.popover-content',true).first();
17165     },
17166     
17167     getAutoCreate : function(){
17168          
17169         var cfg = {
17170            cls : 'popover roo-dynamic',
17171            style: 'display:block',
17172            cn : [
17173                 {
17174                     cls : 'arrow'
17175                 },
17176                 {
17177                     cls : 'popover-inner',
17178                     cn : [
17179                         {
17180                             tag: 'h3',
17181                             cls: 'popover-title',
17182                             html : this.title
17183                         },
17184                         {
17185                             cls : 'popover-content',
17186                             html : this.html
17187                         }
17188                     ]
17189                     
17190                 }
17191            ]
17192         };
17193         
17194         return cfg;
17195     },
17196     setTitle: function(str)
17197     {
17198         this.title = str;
17199         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17200     },
17201     setContent: function(str)
17202     {
17203         this.html = str;
17204         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17205     },
17206     // as it get's added to the bottom of the page.
17207     onRender : function(ct, position)
17208     {
17209         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17210         if(!this.el){
17211             var cfg = Roo.apply({},  this.getAutoCreate());
17212             cfg.id = Roo.id();
17213             
17214             if (this.cls) {
17215                 cfg.cls += ' ' + this.cls;
17216             }
17217             if (this.style) {
17218                 cfg.style = this.style;
17219             }
17220             //Roo.log("adding to ");
17221             this.el = Roo.get(document.body).createChild(cfg, position);
17222 //            Roo.log(this.el);
17223         }
17224         this.initEvents();
17225     },
17226     
17227     initEvents : function()
17228     {
17229         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17230         this.el.enableDisplayMode('block');
17231         this.el.hide();
17232         if (this.over === false) {
17233             return; 
17234         }
17235         if (this.triggers === false) {
17236             return;
17237         }
17238         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17239         var triggers = this.trigger ? this.trigger.split(' ') : [];
17240         Roo.each(triggers, function(trigger) {
17241         
17242             if (trigger == 'click') {
17243                 on_el.on('click', this.toggle, this);
17244             } else if (trigger != 'manual') {
17245                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17246                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17247       
17248                 on_el.on(eventIn  ,this.enter, this);
17249                 on_el.on(eventOut, this.leave, this);
17250             }
17251         }, this);
17252         
17253     },
17254     
17255     
17256     // private
17257     timeout : null,
17258     hoverState : null,
17259     
17260     toggle : function () {
17261         this.hoverState == 'in' ? this.leave() : this.enter();
17262     },
17263     
17264     enter : function () {
17265         
17266         clearTimeout(this.timeout);
17267     
17268         this.hoverState = 'in';
17269     
17270         if (!this.delay || !this.delay.show) {
17271             this.show();
17272             return;
17273         }
17274         var _t = this;
17275         this.timeout = setTimeout(function () {
17276             if (_t.hoverState == 'in') {
17277                 _t.show();
17278             }
17279         }, this.delay.show)
17280     },
17281     
17282     leave : function() {
17283         clearTimeout(this.timeout);
17284     
17285         this.hoverState = 'out';
17286     
17287         if (!this.delay || !this.delay.hide) {
17288             this.hide();
17289             return;
17290         }
17291         var _t = this;
17292         this.timeout = setTimeout(function () {
17293             if (_t.hoverState == 'out') {
17294                 _t.hide();
17295             }
17296         }, this.delay.hide)
17297     },
17298     
17299     show : function (on_el)
17300     {
17301         if (!on_el) {
17302             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17303         }
17304         
17305         // set content.
17306         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17307         if (this.html !== false) {
17308             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17309         }
17310         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17311         if (!this.title.length) {
17312             this.el.select('.popover-title',true).hide();
17313         }
17314         
17315         var placement = typeof this.placement == 'function' ?
17316             this.placement.call(this, this.el, on_el) :
17317             this.placement;
17318             
17319         var autoToken = /\s?auto?\s?/i;
17320         var autoPlace = autoToken.test(placement);
17321         if (autoPlace) {
17322             placement = placement.replace(autoToken, '') || 'top';
17323         }
17324         
17325         //this.el.detach()
17326         //this.el.setXY([0,0]);
17327         this.el.show();
17328         this.el.dom.style.display='block';
17329         this.el.addClass(placement);
17330         
17331         //this.el.appendTo(on_el);
17332         
17333         var p = this.getPosition();
17334         var box = this.el.getBox();
17335         
17336         if (autoPlace) {
17337             // fixme..
17338         }
17339         var align = Roo.bootstrap.Popover.alignment[placement];
17340         
17341 //        Roo.log(align);
17342         this.el.alignTo(on_el, align[0],align[1]);
17343         //var arrow = this.el.select('.arrow',true).first();
17344         //arrow.set(align[2], 
17345         
17346         this.el.addClass('in');
17347         
17348         
17349         if (this.el.hasClass('fade')) {
17350             // fade it?
17351         }
17352         
17353         this.hoverState = 'in';
17354         
17355         this.fireEvent('show', this);
17356         
17357     },
17358     hide : function()
17359     {
17360         this.el.setXY([0,0]);
17361         this.el.removeClass('in');
17362         this.el.hide();
17363         this.hoverState = null;
17364         
17365         this.fireEvent('hide', this);
17366     }
17367     
17368 });
17369
17370 Roo.bootstrap.Popover.alignment = {
17371     'left' : ['r-l', [-10,0], 'right'],
17372     'right' : ['l-r', [10,0], 'left'],
17373     'bottom' : ['t-b', [0,10], 'top'],
17374     'top' : [ 'b-t', [0,-10], 'bottom']
17375 };
17376
17377  /*
17378  * - LGPL
17379  *
17380  * Progress
17381  * 
17382  */
17383
17384 /**
17385  * @class Roo.bootstrap.Progress
17386  * @extends Roo.bootstrap.Component
17387  * Bootstrap Progress class
17388  * @cfg {Boolean} striped striped of the progress bar
17389  * @cfg {Boolean} active animated of the progress bar
17390  * 
17391  * 
17392  * @constructor
17393  * Create a new Progress
17394  * @param {Object} config The config object
17395  */
17396
17397 Roo.bootstrap.Progress = function(config){
17398     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17399 };
17400
17401 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17402     
17403     striped : false,
17404     active: false,
17405     
17406     getAutoCreate : function(){
17407         var cfg = {
17408             tag: 'div',
17409             cls: 'progress'
17410         };
17411         
17412         
17413         if(this.striped){
17414             cfg.cls += ' progress-striped';
17415         }
17416       
17417         if(this.active){
17418             cfg.cls += ' active';
17419         }
17420         
17421         
17422         return cfg;
17423     }
17424    
17425 });
17426
17427  
17428
17429  /*
17430  * - LGPL
17431  *
17432  * ProgressBar
17433  * 
17434  */
17435
17436 /**
17437  * @class Roo.bootstrap.ProgressBar
17438  * @extends Roo.bootstrap.Component
17439  * Bootstrap ProgressBar class
17440  * @cfg {Number} aria_valuenow aria-value now
17441  * @cfg {Number} aria_valuemin aria-value min
17442  * @cfg {Number} aria_valuemax aria-value max
17443  * @cfg {String} label label for the progress bar
17444  * @cfg {String} panel (success | info | warning | danger )
17445  * @cfg {String} role role of the progress bar
17446  * @cfg {String} sr_only text
17447  * 
17448  * 
17449  * @constructor
17450  * Create a new ProgressBar
17451  * @param {Object} config The config object
17452  */
17453
17454 Roo.bootstrap.ProgressBar = function(config){
17455     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17456 };
17457
17458 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17459     
17460     aria_valuenow : 0,
17461     aria_valuemin : 0,
17462     aria_valuemax : 100,
17463     label : false,
17464     panel : false,
17465     role : false,
17466     sr_only: false,
17467     
17468     getAutoCreate : function()
17469     {
17470         
17471         var cfg = {
17472             tag: 'div',
17473             cls: 'progress-bar',
17474             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17475         };
17476         
17477         if(this.sr_only){
17478             cfg.cn = {
17479                 tag: 'span',
17480                 cls: 'sr-only',
17481                 html: this.sr_only
17482             }
17483         }
17484         
17485         if(this.role){
17486             cfg.role = this.role;
17487         }
17488         
17489         if(this.aria_valuenow){
17490             cfg['aria-valuenow'] = this.aria_valuenow;
17491         }
17492         
17493         if(this.aria_valuemin){
17494             cfg['aria-valuemin'] = this.aria_valuemin;
17495         }
17496         
17497         if(this.aria_valuemax){
17498             cfg['aria-valuemax'] = this.aria_valuemax;
17499         }
17500         
17501         if(this.label && !this.sr_only){
17502             cfg.html = this.label;
17503         }
17504         
17505         if(this.panel){
17506             cfg.cls += ' progress-bar-' + this.panel;
17507         }
17508         
17509         return cfg;
17510     },
17511     
17512     update : function(aria_valuenow)
17513     {
17514         this.aria_valuenow = aria_valuenow;
17515         
17516         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17517     }
17518    
17519 });
17520
17521  
17522
17523  /*
17524  * - LGPL
17525  *
17526  * column
17527  * 
17528  */
17529
17530 /**
17531  * @class Roo.bootstrap.TabGroup
17532  * @extends Roo.bootstrap.Column
17533  * Bootstrap Column class
17534  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17535  * @cfg {Boolean} carousel true to make the group behave like a carousel
17536  * @cfg {Boolean} bullets show bullets for the panels
17537  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17538  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17539  * @cfg {Boolean} showarrow (true|false) show arrow default true
17540  * 
17541  * @constructor
17542  * Create a new TabGroup
17543  * @param {Object} config The config object
17544  */
17545
17546 Roo.bootstrap.TabGroup = function(config){
17547     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17548     if (!this.navId) {
17549         this.navId = Roo.id();
17550     }
17551     this.tabs = [];
17552     Roo.bootstrap.TabGroup.register(this);
17553     
17554 };
17555
17556 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17557     
17558     carousel : false,
17559     transition : false,
17560     bullets : 0,
17561     timer : 0,
17562     autoslide : false,
17563     slideFn : false,
17564     slideOnTouch : false,
17565     showarrow : true,
17566     
17567     getAutoCreate : function()
17568     {
17569         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17570         
17571         cfg.cls += ' tab-content';
17572         
17573         if (this.carousel) {
17574             cfg.cls += ' carousel slide';
17575             
17576             cfg.cn = [{
17577                cls : 'carousel-inner',
17578                cn : []
17579             }];
17580         
17581             if(this.bullets  && !Roo.isTouch){
17582                 
17583                 var bullets = {
17584                     cls : 'carousel-bullets',
17585                     cn : []
17586                 };
17587                
17588                 if(this.bullets_cls){
17589                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17590                 }
17591                 
17592                 bullets.cn.push({
17593                     cls : 'clear'
17594                 });
17595                 
17596                 cfg.cn[0].cn.push(bullets);
17597             }
17598             
17599             if(this.showarrow){
17600                 cfg.cn[0].cn.push({
17601                     tag : 'div',
17602                     class : 'carousel-arrow',
17603                     cn : [
17604                         {
17605                             tag : 'div',
17606                             class : 'carousel-prev',
17607                             cn : [
17608                                 {
17609                                     tag : 'i',
17610                                     class : 'fa fa-chevron-left'
17611                                 }
17612                             ]
17613                         },
17614                         {
17615                             tag : 'div',
17616                             class : 'carousel-next',
17617                             cn : [
17618                                 {
17619                                     tag : 'i',
17620                                     class : 'fa fa-chevron-right'
17621                                 }
17622                             ]
17623                         }
17624                     ]
17625                 });
17626             }
17627             
17628         }
17629         
17630         return cfg;
17631     },
17632     
17633     initEvents:  function()
17634     {
17635 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17636 //            this.el.on("touchstart", this.onTouchStart, this);
17637 //        }
17638         
17639         if(this.autoslide){
17640             var _this = this;
17641             
17642             this.slideFn = window.setInterval(function() {
17643                 _this.showPanelNext();
17644             }, this.timer);
17645         }
17646         
17647         if(this.showarrow){
17648             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17649             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17650         }
17651         
17652         
17653     },
17654     
17655 //    onTouchStart : function(e, el, o)
17656 //    {
17657 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17658 //            return;
17659 //        }
17660 //        
17661 //        this.showPanelNext();
17662 //    },
17663     
17664     
17665     getChildContainer : function()
17666     {
17667         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17668     },
17669     
17670     /**
17671     * register a Navigation item
17672     * @param {Roo.bootstrap.NavItem} the navitem to add
17673     */
17674     register : function(item)
17675     {
17676         this.tabs.push( item);
17677         item.navId = this.navId; // not really needed..
17678         this.addBullet();
17679     
17680     },
17681     
17682     getActivePanel : function()
17683     {
17684         var r = false;
17685         Roo.each(this.tabs, function(t) {
17686             if (t.active) {
17687                 r = t;
17688                 return false;
17689             }
17690             return null;
17691         });
17692         return r;
17693         
17694     },
17695     getPanelByName : function(n)
17696     {
17697         var r = false;
17698         Roo.each(this.tabs, function(t) {
17699             if (t.tabId == n) {
17700                 r = t;
17701                 return false;
17702             }
17703             return null;
17704         });
17705         return r;
17706     },
17707     indexOfPanel : function(p)
17708     {
17709         var r = false;
17710         Roo.each(this.tabs, function(t,i) {
17711             if (t.tabId == p.tabId) {
17712                 r = i;
17713                 return false;
17714             }
17715             return null;
17716         });
17717         return r;
17718     },
17719     /**
17720      * show a specific panel
17721      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17722      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17723      */
17724     showPanel : function (pan)
17725     {
17726         if(this.transition || typeof(pan) == 'undefined'){
17727             Roo.log("waiting for the transitionend");
17728             return;
17729         }
17730         
17731         if (typeof(pan) == 'number') {
17732             pan = this.tabs[pan];
17733         }
17734         
17735         if (typeof(pan) == 'string') {
17736             pan = this.getPanelByName(pan);
17737         }
17738         
17739         var cur = this.getActivePanel();
17740         
17741         if(!pan || !cur){
17742             Roo.log('pan or acitve pan is undefined');
17743             return false;
17744         }
17745         
17746         if (pan.tabId == this.getActivePanel().tabId) {
17747             return true;
17748         }
17749         
17750         if (false === cur.fireEvent('beforedeactivate')) {
17751             return false;
17752         }
17753         
17754         if(this.bullets > 0 && !Roo.isTouch){
17755             this.setActiveBullet(this.indexOfPanel(pan));
17756         }
17757         
17758         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17759             
17760             this.transition = true;
17761             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17762             var lr = dir == 'next' ? 'left' : 'right';
17763             pan.el.addClass(dir); // or prev
17764             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17765             cur.el.addClass(lr); // or right
17766             pan.el.addClass(lr);
17767             
17768             var _this = this;
17769             cur.el.on('transitionend', function() {
17770                 Roo.log("trans end?");
17771                 
17772                 pan.el.removeClass([lr,dir]);
17773                 pan.setActive(true);
17774                 
17775                 cur.el.removeClass([lr]);
17776                 cur.setActive(false);
17777                 
17778                 _this.transition = false;
17779                 
17780             }, this, { single:  true } );
17781             
17782             return true;
17783         }
17784         
17785         cur.setActive(false);
17786         pan.setActive(true);
17787         
17788         return true;
17789         
17790     },
17791     showPanelNext : function()
17792     {
17793         var i = this.indexOfPanel(this.getActivePanel());
17794         
17795         if (i >= this.tabs.length - 1 && !this.autoslide) {
17796             return;
17797         }
17798         
17799         if (i >= this.tabs.length - 1 && this.autoslide) {
17800             i = -1;
17801         }
17802         
17803         this.showPanel(this.tabs[i+1]);
17804     },
17805     
17806     showPanelPrev : function()
17807     {
17808         var i = this.indexOfPanel(this.getActivePanel());
17809         
17810         if (i  < 1 && !this.autoslide) {
17811             return;
17812         }
17813         
17814         if (i < 1 && this.autoslide) {
17815             i = this.tabs.length;
17816         }
17817         
17818         this.showPanel(this.tabs[i-1]);
17819     },
17820     
17821     
17822     addBullet: function()
17823     {
17824         if(!this.bullets || Roo.isTouch){
17825             return;
17826         }
17827         var ctr = this.el.select('.carousel-bullets',true).first();
17828         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17829         var bullet = ctr.createChild({
17830             cls : 'bullet bullet-' + i
17831         },ctr.dom.lastChild);
17832         
17833         
17834         var _this = this;
17835         
17836         bullet.on('click', (function(e, el, o, ii, t){
17837
17838             e.preventDefault();
17839
17840             this.showPanel(ii);
17841
17842             if(this.autoslide && this.slideFn){
17843                 clearInterval(this.slideFn);
17844                 this.slideFn = window.setInterval(function() {
17845                     _this.showPanelNext();
17846                 }, this.timer);
17847             }
17848
17849         }).createDelegate(this, [i, bullet], true));
17850                 
17851         
17852     },
17853      
17854     setActiveBullet : function(i)
17855     {
17856         if(Roo.isTouch){
17857             return;
17858         }
17859         
17860         Roo.each(this.el.select('.bullet', true).elements, function(el){
17861             el.removeClass('selected');
17862         });
17863
17864         var bullet = this.el.select('.bullet-' + i, true).first();
17865         
17866         if(!bullet){
17867             return;
17868         }
17869         
17870         bullet.addClass('selected');
17871     }
17872     
17873     
17874   
17875 });
17876
17877  
17878
17879  
17880  
17881 Roo.apply(Roo.bootstrap.TabGroup, {
17882     
17883     groups: {},
17884      /**
17885     * register a Navigation Group
17886     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17887     */
17888     register : function(navgrp)
17889     {
17890         this.groups[navgrp.navId] = navgrp;
17891         
17892     },
17893     /**
17894     * fetch a Navigation Group based on the navigation ID
17895     * if one does not exist , it will get created.
17896     * @param {string} the navgroup to add
17897     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17898     */
17899     get: function(navId) {
17900         if (typeof(this.groups[navId]) == 'undefined') {
17901             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17902         }
17903         return this.groups[navId] ;
17904     }
17905     
17906     
17907     
17908 });
17909
17910  /*
17911  * - LGPL
17912  *
17913  * TabPanel
17914  * 
17915  */
17916
17917 /**
17918  * @class Roo.bootstrap.TabPanel
17919  * @extends Roo.bootstrap.Component
17920  * Bootstrap TabPanel class
17921  * @cfg {Boolean} active panel active
17922  * @cfg {String} html panel content
17923  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17924  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17925  * @cfg {String} href click to link..
17926  * 
17927  * 
17928  * @constructor
17929  * Create a new TabPanel
17930  * @param {Object} config The config object
17931  */
17932
17933 Roo.bootstrap.TabPanel = function(config){
17934     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17935     this.addEvents({
17936         /**
17937              * @event changed
17938              * Fires when the active status changes
17939              * @param {Roo.bootstrap.TabPanel} this
17940              * @param {Boolean} state the new state
17941             
17942          */
17943         'changed': true,
17944         /**
17945              * @event beforedeactivate
17946              * Fires before a tab is de-activated - can be used to do validation on a form.
17947              * @param {Roo.bootstrap.TabPanel} this
17948              * @return {Boolean} false if there is an error
17949             
17950          */
17951         'beforedeactivate': true
17952      });
17953     
17954     this.tabId = this.tabId || Roo.id();
17955   
17956 };
17957
17958 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17959     
17960     active: false,
17961     html: false,
17962     tabId: false,
17963     navId : false,
17964     href : '',
17965     
17966     getAutoCreate : function(){
17967         var cfg = {
17968             tag: 'div',
17969             // item is needed for carousel - not sure if it has any effect otherwise
17970             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17971             html: this.html || ''
17972         };
17973         
17974         if(this.active){
17975             cfg.cls += ' active';
17976         }
17977         
17978         if(this.tabId){
17979             cfg.tabId = this.tabId;
17980         }
17981         
17982         
17983         return cfg;
17984     },
17985     
17986     initEvents:  function()
17987     {
17988         var p = this.parent();
17989         
17990         this.navId = this.navId || p.navId;
17991         
17992         if (typeof(this.navId) != 'undefined') {
17993             // not really needed.. but just in case.. parent should be a NavGroup.
17994             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17995             
17996             tg.register(this);
17997             
17998             var i = tg.tabs.length - 1;
17999             
18000             if(this.active && tg.bullets > 0 && i < tg.bullets){
18001                 tg.setActiveBullet(i);
18002             }
18003         }
18004         
18005         this.el.on('click', this.onClick, this);
18006         
18007         if(Roo.isTouch){
18008             this.el.on("touchstart", this.onTouchStart, this);
18009             this.el.on("touchmove", this.onTouchMove, this);
18010             this.el.on("touchend", this.onTouchEnd, this);
18011         }
18012         
18013     },
18014     
18015     onRender : function(ct, position)
18016     {
18017         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18018     },
18019     
18020     setActive : function(state)
18021     {
18022         Roo.log("panel - set active " + this.tabId + "=" + state);
18023         
18024         this.active = state;
18025         if (!state) {
18026             this.el.removeClass('active');
18027             
18028         } else  if (!this.el.hasClass('active')) {
18029             this.el.addClass('active');
18030         }
18031         
18032         this.fireEvent('changed', this, state);
18033     },
18034     
18035     onClick : function(e)
18036     {
18037         e.preventDefault();
18038         
18039         if(!this.href.length){
18040             return;
18041         }
18042         
18043         window.location.href = this.href;
18044     },
18045     
18046     startX : 0,
18047     startY : 0,
18048     endX : 0,
18049     endY : 0,
18050     swiping : false,
18051     
18052     onTouchStart : function(e)
18053     {
18054         this.swiping = false;
18055         
18056         this.startX = e.browserEvent.touches[0].clientX;
18057         this.startY = e.browserEvent.touches[0].clientY;
18058     },
18059     
18060     onTouchMove : function(e)
18061     {
18062         this.swiping = true;
18063         
18064         this.endX = e.browserEvent.touches[0].clientX;
18065         this.endY = e.browserEvent.touches[0].clientY;
18066     },
18067     
18068     onTouchEnd : function(e)
18069     {
18070         if(!this.swiping){
18071             this.onClick(e);
18072             return;
18073         }
18074         
18075         var tabGroup = this.parent();
18076         
18077         if(this.endX > this.startX){ // swiping right
18078             tabGroup.showPanelPrev();
18079             return;
18080         }
18081         
18082         if(this.startX > this.endX){ // swiping left
18083             tabGroup.showPanelNext();
18084             return;
18085         }
18086     }
18087     
18088     
18089 });
18090  
18091
18092  
18093
18094  /*
18095  * - LGPL
18096  *
18097  * DateField
18098  * 
18099  */
18100
18101 /**
18102  * @class Roo.bootstrap.DateField
18103  * @extends Roo.bootstrap.Input
18104  * Bootstrap DateField class
18105  * @cfg {Number} weekStart default 0
18106  * @cfg {String} viewMode default empty, (months|years)
18107  * @cfg {String} minViewMode default empty, (months|years)
18108  * @cfg {Number} startDate default -Infinity
18109  * @cfg {Number} endDate default Infinity
18110  * @cfg {Boolean} todayHighlight default false
18111  * @cfg {Boolean} todayBtn default false
18112  * @cfg {Boolean} calendarWeeks default false
18113  * @cfg {Object} daysOfWeekDisabled default empty
18114  * @cfg {Boolean} singleMode default false (true | false)
18115  * 
18116  * @cfg {Boolean} keyboardNavigation default true
18117  * @cfg {String} language default en
18118  * 
18119  * @constructor
18120  * Create a new DateField
18121  * @param {Object} config The config object
18122  */
18123
18124 Roo.bootstrap.DateField = function(config){
18125     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18126      this.addEvents({
18127             /**
18128              * @event show
18129              * Fires when this field show.
18130              * @param {Roo.bootstrap.DateField} this
18131              * @param {Mixed} date The date value
18132              */
18133             show : true,
18134             /**
18135              * @event show
18136              * Fires when this field hide.
18137              * @param {Roo.bootstrap.DateField} this
18138              * @param {Mixed} date The date value
18139              */
18140             hide : true,
18141             /**
18142              * @event select
18143              * Fires when select a date.
18144              * @param {Roo.bootstrap.DateField} this
18145              * @param {Mixed} date The date value
18146              */
18147             select : true,
18148             /**
18149              * @event beforeselect
18150              * Fires when before select a date.
18151              * @param {Roo.bootstrap.DateField} this
18152              * @param {Mixed} date The date value
18153              */
18154             beforeselect : true
18155         });
18156 };
18157
18158 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18159     
18160     /**
18161      * @cfg {String} format
18162      * The default date format string which can be overriden for localization support.  The format must be
18163      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18164      */
18165     format : "m/d/y",
18166     /**
18167      * @cfg {String} altFormats
18168      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18169      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18170      */
18171     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18172     
18173     weekStart : 0,
18174     
18175     viewMode : '',
18176     
18177     minViewMode : '',
18178     
18179     todayHighlight : false,
18180     
18181     todayBtn: false,
18182     
18183     language: 'en',
18184     
18185     keyboardNavigation: true,
18186     
18187     calendarWeeks: false,
18188     
18189     startDate: -Infinity,
18190     
18191     endDate: Infinity,
18192     
18193     daysOfWeekDisabled: [],
18194     
18195     _events: [],
18196     
18197     singleMode : false,
18198     
18199     UTCDate: function()
18200     {
18201         return new Date(Date.UTC.apply(Date, arguments));
18202     },
18203     
18204     UTCToday: function()
18205     {
18206         var today = new Date();
18207         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18208     },
18209     
18210     getDate: function() {
18211             var d = this.getUTCDate();
18212             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18213     },
18214     
18215     getUTCDate: function() {
18216             return this.date;
18217     },
18218     
18219     setDate: function(d) {
18220             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18221     },
18222     
18223     setUTCDate: function(d) {
18224             this.date = d;
18225             this.setValue(this.formatDate(this.date));
18226     },
18227         
18228     onRender: function(ct, position)
18229     {
18230         
18231         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18232         
18233         this.language = this.language || 'en';
18234         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18235         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18236         
18237         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18238         this.format = this.format || 'm/d/y';
18239         this.isInline = false;
18240         this.isInput = true;
18241         this.component = this.el.select('.add-on', true).first() || false;
18242         this.component = (this.component && this.component.length === 0) ? false : this.component;
18243         this.hasInput = this.component && this.inputEl().length;
18244         
18245         if (typeof(this.minViewMode === 'string')) {
18246             switch (this.minViewMode) {
18247                 case 'months':
18248                     this.minViewMode = 1;
18249                     break;
18250                 case 'years':
18251                     this.minViewMode = 2;
18252                     break;
18253                 default:
18254                     this.minViewMode = 0;
18255                     break;
18256             }
18257         }
18258         
18259         if (typeof(this.viewMode === 'string')) {
18260             switch (this.viewMode) {
18261                 case 'months':
18262                     this.viewMode = 1;
18263                     break;
18264                 case 'years':
18265                     this.viewMode = 2;
18266                     break;
18267                 default:
18268                     this.viewMode = 0;
18269                     break;
18270             }
18271         }
18272                 
18273         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18274         
18275 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18276         
18277         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18278         
18279         this.picker().on('mousedown', this.onMousedown, this);
18280         this.picker().on('click', this.onClick, this);
18281         
18282         this.picker().addClass('datepicker-dropdown');
18283         
18284         this.startViewMode = this.viewMode;
18285         
18286         if(this.singleMode){
18287             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18288                 v.setVisibilityMode(Roo.Element.DISPLAY);
18289                 v.hide();
18290             });
18291             
18292             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18293                 v.setStyle('width', '189px');
18294             });
18295         }
18296         
18297         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18298             if(!this.calendarWeeks){
18299                 v.remove();
18300                 return;
18301             }
18302             
18303             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18304             v.attr('colspan', function(i, val){
18305                 return parseInt(val) + 1;
18306             });
18307         });
18308                         
18309         
18310         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18311         
18312         this.setStartDate(this.startDate);
18313         this.setEndDate(this.endDate);
18314         
18315         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18316         
18317         this.fillDow();
18318         this.fillMonths();
18319         this.update();
18320         this.showMode();
18321         
18322         if(this.isInline) {
18323             this.show();
18324         }
18325     },
18326     
18327     picker : function()
18328     {
18329         return this.pickerEl;
18330 //        return this.el.select('.datepicker', true).first();
18331     },
18332     
18333     fillDow: function()
18334     {
18335         var dowCnt = this.weekStart;
18336         
18337         var dow = {
18338             tag: 'tr',
18339             cn: [
18340                 
18341             ]
18342         };
18343         
18344         if(this.calendarWeeks){
18345             dow.cn.push({
18346                 tag: 'th',
18347                 cls: 'cw',
18348                 html: '&nbsp;'
18349             })
18350         }
18351         
18352         while (dowCnt < this.weekStart + 7) {
18353             dow.cn.push({
18354                 tag: 'th',
18355                 cls: 'dow',
18356                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18357             });
18358         }
18359         
18360         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18361     },
18362     
18363     fillMonths: function()
18364     {    
18365         var i = 0;
18366         var months = this.picker().select('>.datepicker-months td', true).first();
18367         
18368         months.dom.innerHTML = '';
18369         
18370         while (i < 12) {
18371             var month = {
18372                 tag: 'span',
18373                 cls: 'month',
18374                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18375             };
18376             
18377             months.createChild(month);
18378         }
18379         
18380     },
18381     
18382     update: function()
18383     {
18384         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;
18385         
18386         if (this.date < this.startDate) {
18387             this.viewDate = new Date(this.startDate);
18388         } else if (this.date > this.endDate) {
18389             this.viewDate = new Date(this.endDate);
18390         } else {
18391             this.viewDate = new Date(this.date);
18392         }
18393         
18394         this.fill();
18395     },
18396     
18397     fill: function() 
18398     {
18399         var d = new Date(this.viewDate),
18400                 year = d.getUTCFullYear(),
18401                 month = d.getUTCMonth(),
18402                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18403                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18404                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18405                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18406                 currentDate = this.date && this.date.valueOf(),
18407                 today = this.UTCToday();
18408         
18409         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18410         
18411 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18412         
18413 //        this.picker.select('>tfoot th.today').
18414 //                                              .text(dates[this.language].today)
18415 //                                              .toggle(this.todayBtn !== false);
18416     
18417         this.updateNavArrows();
18418         this.fillMonths();
18419                                                 
18420         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18421         
18422         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18423          
18424         prevMonth.setUTCDate(day);
18425         
18426         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18427         
18428         var nextMonth = new Date(prevMonth);
18429         
18430         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18431         
18432         nextMonth = nextMonth.valueOf();
18433         
18434         var fillMonths = false;
18435         
18436         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18437         
18438         while(prevMonth.valueOf() < nextMonth) {
18439             var clsName = '';
18440             
18441             if (prevMonth.getUTCDay() === this.weekStart) {
18442                 if(fillMonths){
18443                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18444                 }
18445                     
18446                 fillMonths = {
18447                     tag: 'tr',
18448                     cn: []
18449                 };
18450                 
18451                 if(this.calendarWeeks){
18452                     // ISO 8601: First week contains first thursday.
18453                     // ISO also states week starts on Monday, but we can be more abstract here.
18454                     var
18455                     // Start of current week: based on weekstart/current date
18456                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18457                     // Thursday of this week
18458                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18459                     // First Thursday of year, year from thursday
18460                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18461                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18462                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18463                     
18464                     fillMonths.cn.push({
18465                         tag: 'td',
18466                         cls: 'cw',
18467                         html: calWeek
18468                     });
18469                 }
18470             }
18471             
18472             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18473                 clsName += ' old';
18474             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18475                 clsName += ' new';
18476             }
18477             if (this.todayHighlight &&
18478                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18479                 prevMonth.getUTCMonth() == today.getMonth() &&
18480                 prevMonth.getUTCDate() == today.getDate()) {
18481                 clsName += ' today';
18482             }
18483             
18484             if (currentDate && prevMonth.valueOf() === currentDate) {
18485                 clsName += ' active';
18486             }
18487             
18488             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18489                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18490                     clsName += ' disabled';
18491             }
18492             
18493             fillMonths.cn.push({
18494                 tag: 'td',
18495                 cls: 'day ' + clsName,
18496                 html: prevMonth.getDate()
18497             });
18498             
18499             prevMonth.setDate(prevMonth.getDate()+1);
18500         }
18501           
18502         var currentYear = this.date && this.date.getUTCFullYear();
18503         var currentMonth = this.date && this.date.getUTCMonth();
18504         
18505         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18506         
18507         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18508             v.removeClass('active');
18509             
18510             if(currentYear === year && k === currentMonth){
18511                 v.addClass('active');
18512             }
18513             
18514             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18515                 v.addClass('disabled');
18516             }
18517             
18518         });
18519         
18520         
18521         year = parseInt(year/10, 10) * 10;
18522         
18523         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18524         
18525         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18526         
18527         year -= 1;
18528         for (var i = -1; i < 11; i++) {
18529             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18530                 tag: 'span',
18531                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18532                 html: year
18533             });
18534             
18535             year += 1;
18536         }
18537     },
18538     
18539     showMode: function(dir) 
18540     {
18541         if (dir) {
18542             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18543         }
18544         
18545         Roo.each(this.picker().select('>div',true).elements, function(v){
18546             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18547             v.hide();
18548         });
18549         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18550     },
18551     
18552     place: function()
18553     {
18554         if(this.isInline) {
18555             return;
18556         }
18557         
18558         this.picker().removeClass(['bottom', 'top']);
18559         
18560         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18561             /*
18562              * place to the top of element!
18563              *
18564              */
18565             
18566             this.picker().addClass('top');
18567             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18568             
18569             return;
18570         }
18571         
18572         this.picker().addClass('bottom');
18573         
18574         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18575     },
18576     
18577     parseDate : function(value)
18578     {
18579         if(!value || value instanceof Date){
18580             return value;
18581         }
18582         var v = Date.parseDate(value, this.format);
18583         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18584             v = Date.parseDate(value, 'Y-m-d');
18585         }
18586         if(!v && this.altFormats){
18587             if(!this.altFormatsArray){
18588                 this.altFormatsArray = this.altFormats.split("|");
18589             }
18590             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18591                 v = Date.parseDate(value, this.altFormatsArray[i]);
18592             }
18593         }
18594         return v;
18595     },
18596     
18597     formatDate : function(date, fmt)
18598     {   
18599         return (!date || !(date instanceof Date)) ?
18600         date : date.dateFormat(fmt || this.format);
18601     },
18602     
18603     onFocus : function()
18604     {
18605         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18606         this.show();
18607     },
18608     
18609     onBlur : function()
18610     {
18611         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18612         
18613         var d = this.inputEl().getValue();
18614         
18615         this.setValue(d);
18616                 
18617         this.hide();
18618     },
18619     
18620     show : function()
18621     {
18622         this.picker().show();
18623         this.update();
18624         this.place();
18625         
18626         this.fireEvent('show', this, this.date);
18627     },
18628     
18629     hide : function()
18630     {
18631         if(this.isInline) {
18632             return;
18633         }
18634         this.picker().hide();
18635         this.viewMode = this.startViewMode;
18636         this.showMode();
18637         
18638         this.fireEvent('hide', this, this.date);
18639         
18640     },
18641     
18642     onMousedown: function(e)
18643     {
18644         e.stopPropagation();
18645         e.preventDefault();
18646     },
18647     
18648     keyup: function(e)
18649     {
18650         Roo.bootstrap.DateField.superclass.keyup.call(this);
18651         this.update();
18652     },
18653
18654     setValue: function(v)
18655     {
18656         if(this.fireEvent('beforeselect', this, v) !== false){
18657             var d = new Date(this.parseDate(v) ).clearTime();
18658         
18659             if(isNaN(d.getTime())){
18660                 this.date = this.viewDate = '';
18661                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18662                 return;
18663             }
18664
18665             v = this.formatDate(d);
18666
18667             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18668
18669             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18670
18671             this.update();
18672
18673             this.fireEvent('select', this, this.date);
18674         }
18675     },
18676     
18677     getValue: function()
18678     {
18679         return this.formatDate(this.date);
18680     },
18681     
18682     fireKey: function(e)
18683     {
18684         if (!this.picker().isVisible()){
18685             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18686                 this.show();
18687             }
18688             return;
18689         }
18690         
18691         var dateChanged = false,
18692         dir, day, month,
18693         newDate, newViewDate;
18694         
18695         switch(e.keyCode){
18696             case 27: // escape
18697                 this.hide();
18698                 e.preventDefault();
18699                 break;
18700             case 37: // left
18701             case 39: // right
18702                 if (!this.keyboardNavigation) {
18703                     break;
18704                 }
18705                 dir = e.keyCode == 37 ? -1 : 1;
18706                 
18707                 if (e.ctrlKey){
18708                     newDate = this.moveYear(this.date, dir);
18709                     newViewDate = this.moveYear(this.viewDate, dir);
18710                 } else if (e.shiftKey){
18711                     newDate = this.moveMonth(this.date, dir);
18712                     newViewDate = this.moveMonth(this.viewDate, dir);
18713                 } else {
18714                     newDate = new Date(this.date);
18715                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18716                     newViewDate = new Date(this.viewDate);
18717                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18718                 }
18719                 if (this.dateWithinRange(newDate)){
18720                     this.date = newDate;
18721                     this.viewDate = newViewDate;
18722                     this.setValue(this.formatDate(this.date));
18723 //                    this.update();
18724                     e.preventDefault();
18725                     dateChanged = true;
18726                 }
18727                 break;
18728             case 38: // up
18729             case 40: // down
18730                 if (!this.keyboardNavigation) {
18731                     break;
18732                 }
18733                 dir = e.keyCode == 38 ? -1 : 1;
18734                 if (e.ctrlKey){
18735                     newDate = this.moveYear(this.date, dir);
18736                     newViewDate = this.moveYear(this.viewDate, dir);
18737                 } else if (e.shiftKey){
18738                     newDate = this.moveMonth(this.date, dir);
18739                     newViewDate = this.moveMonth(this.viewDate, dir);
18740                 } else {
18741                     newDate = new Date(this.date);
18742                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18743                     newViewDate = new Date(this.viewDate);
18744                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18745                 }
18746                 if (this.dateWithinRange(newDate)){
18747                     this.date = newDate;
18748                     this.viewDate = newViewDate;
18749                     this.setValue(this.formatDate(this.date));
18750 //                    this.update();
18751                     e.preventDefault();
18752                     dateChanged = true;
18753                 }
18754                 break;
18755             case 13: // enter
18756                 this.setValue(this.formatDate(this.date));
18757                 this.hide();
18758                 e.preventDefault();
18759                 break;
18760             case 9: // tab
18761                 this.setValue(this.formatDate(this.date));
18762                 this.hide();
18763                 break;
18764             case 16: // shift
18765             case 17: // ctrl
18766             case 18: // alt
18767                 break;
18768             default :
18769                 this.hide();
18770                 
18771         }
18772     },
18773     
18774     
18775     onClick: function(e) 
18776     {
18777         e.stopPropagation();
18778         e.preventDefault();
18779         
18780         var target = e.getTarget();
18781         
18782         if(target.nodeName.toLowerCase() === 'i'){
18783             target = Roo.get(target).dom.parentNode;
18784         }
18785         
18786         var nodeName = target.nodeName;
18787         var className = target.className;
18788         var html = target.innerHTML;
18789         //Roo.log(nodeName);
18790         
18791         switch(nodeName.toLowerCase()) {
18792             case 'th':
18793                 switch(className) {
18794                     case 'switch':
18795                         this.showMode(1);
18796                         break;
18797                     case 'prev':
18798                     case 'next':
18799                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18800                         switch(this.viewMode){
18801                                 case 0:
18802                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18803                                         break;
18804                                 case 1:
18805                                 case 2:
18806                                         this.viewDate = this.moveYear(this.viewDate, dir);
18807                                         break;
18808                         }
18809                         this.fill();
18810                         break;
18811                     case 'today':
18812                         var date = new Date();
18813                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18814 //                        this.fill()
18815                         this.setValue(this.formatDate(this.date));
18816                         
18817                         this.hide();
18818                         break;
18819                 }
18820                 break;
18821             case 'span':
18822                 if (className.indexOf('disabled') < 0) {
18823                     this.viewDate.setUTCDate(1);
18824                     if (className.indexOf('month') > -1) {
18825                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18826                     } else {
18827                         var year = parseInt(html, 10) || 0;
18828                         this.viewDate.setUTCFullYear(year);
18829                         
18830                     }
18831                     
18832                     if(this.singleMode){
18833                         this.setValue(this.formatDate(this.viewDate));
18834                         this.hide();
18835                         return;
18836                     }
18837                     
18838                     this.showMode(-1);
18839                     this.fill();
18840                 }
18841                 break;
18842                 
18843             case 'td':
18844                 //Roo.log(className);
18845                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18846                     var day = parseInt(html, 10) || 1;
18847                     var year = this.viewDate.getUTCFullYear(),
18848                         month = this.viewDate.getUTCMonth();
18849
18850                     if (className.indexOf('old') > -1) {
18851                         if(month === 0 ){
18852                             month = 11;
18853                             year -= 1;
18854                         }else{
18855                             month -= 1;
18856                         }
18857                     } else if (className.indexOf('new') > -1) {
18858                         if (month == 11) {
18859                             month = 0;
18860                             year += 1;
18861                         } else {
18862                             month += 1;
18863                         }
18864                     }
18865                     //Roo.log([year,month,day]);
18866                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18867                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18868 //                    this.fill();
18869                     //Roo.log(this.formatDate(this.date));
18870                     this.setValue(this.formatDate(this.date));
18871                     this.hide();
18872                 }
18873                 break;
18874         }
18875     },
18876     
18877     setStartDate: function(startDate)
18878     {
18879         this.startDate = startDate || -Infinity;
18880         if (this.startDate !== -Infinity) {
18881             this.startDate = this.parseDate(this.startDate);
18882         }
18883         this.update();
18884         this.updateNavArrows();
18885     },
18886
18887     setEndDate: function(endDate)
18888     {
18889         this.endDate = endDate || Infinity;
18890         if (this.endDate !== Infinity) {
18891             this.endDate = this.parseDate(this.endDate);
18892         }
18893         this.update();
18894         this.updateNavArrows();
18895     },
18896     
18897     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18898     {
18899         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18900         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18901             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18902         }
18903         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18904             return parseInt(d, 10);
18905         });
18906         this.update();
18907         this.updateNavArrows();
18908     },
18909     
18910     updateNavArrows: function() 
18911     {
18912         if(this.singleMode){
18913             return;
18914         }
18915         
18916         var d = new Date(this.viewDate),
18917         year = d.getUTCFullYear(),
18918         month = d.getUTCMonth();
18919         
18920         Roo.each(this.picker().select('.prev', true).elements, function(v){
18921             v.show();
18922             switch (this.viewMode) {
18923                 case 0:
18924
18925                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18926                         v.hide();
18927                     }
18928                     break;
18929                 case 1:
18930                 case 2:
18931                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18932                         v.hide();
18933                     }
18934                     break;
18935             }
18936         });
18937         
18938         Roo.each(this.picker().select('.next', true).elements, function(v){
18939             v.show();
18940             switch (this.viewMode) {
18941                 case 0:
18942
18943                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18944                         v.hide();
18945                     }
18946                     break;
18947                 case 1:
18948                 case 2:
18949                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18950                         v.hide();
18951                     }
18952                     break;
18953             }
18954         })
18955     },
18956     
18957     moveMonth: function(date, dir)
18958     {
18959         if (!dir) {
18960             return date;
18961         }
18962         var new_date = new Date(date.valueOf()),
18963         day = new_date.getUTCDate(),
18964         month = new_date.getUTCMonth(),
18965         mag = Math.abs(dir),
18966         new_month, test;
18967         dir = dir > 0 ? 1 : -1;
18968         if (mag == 1){
18969             test = dir == -1
18970             // If going back one month, make sure month is not current month
18971             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18972             ? function(){
18973                 return new_date.getUTCMonth() == month;
18974             }
18975             // If going forward one month, make sure month is as expected
18976             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18977             : function(){
18978                 return new_date.getUTCMonth() != new_month;
18979             };
18980             new_month = month + dir;
18981             new_date.setUTCMonth(new_month);
18982             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18983             if (new_month < 0 || new_month > 11) {
18984                 new_month = (new_month + 12) % 12;
18985             }
18986         } else {
18987             // For magnitudes >1, move one month at a time...
18988             for (var i=0; i<mag; i++) {
18989                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18990                 new_date = this.moveMonth(new_date, dir);
18991             }
18992             // ...then reset the day, keeping it in the new month
18993             new_month = new_date.getUTCMonth();
18994             new_date.setUTCDate(day);
18995             test = function(){
18996                 return new_month != new_date.getUTCMonth();
18997             };
18998         }
18999         // Common date-resetting loop -- if date is beyond end of month, make it
19000         // end of month
19001         while (test()){
19002             new_date.setUTCDate(--day);
19003             new_date.setUTCMonth(new_month);
19004         }
19005         return new_date;
19006     },
19007
19008     moveYear: function(date, dir)
19009     {
19010         return this.moveMonth(date, dir*12);
19011     },
19012
19013     dateWithinRange: function(date)
19014     {
19015         return date >= this.startDate && date <= this.endDate;
19016     },
19017
19018     
19019     remove: function() 
19020     {
19021         this.picker().remove();
19022     },
19023     
19024     validateValue : function(value)
19025     {
19026         if(value.length < 1)  {
19027             if(this.allowBlank){
19028                 return true;
19029             }
19030             return false;
19031         }
19032         
19033         if(value.length < this.minLength){
19034             return false;
19035         }
19036         if(value.length > this.maxLength){
19037             return false;
19038         }
19039         if(this.vtype){
19040             var vt = Roo.form.VTypes;
19041             if(!vt[this.vtype](value, this)){
19042                 return false;
19043             }
19044         }
19045         if(typeof this.validator == "function"){
19046             var msg = this.validator(value);
19047             if(msg !== true){
19048                 return false;
19049             }
19050         }
19051         
19052         if(this.regex && !this.regex.test(value)){
19053             return false;
19054         }
19055         
19056         if(typeof(this.parseDate(value)) == 'undefined'){
19057             return false;
19058         }
19059         
19060         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19061             return false;
19062         }      
19063         
19064         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19065             return false;
19066         } 
19067         
19068         
19069         return true;
19070     }
19071    
19072 });
19073
19074 Roo.apply(Roo.bootstrap.DateField,  {
19075     
19076     head : {
19077         tag: 'thead',
19078         cn: [
19079         {
19080             tag: 'tr',
19081             cn: [
19082             {
19083                 tag: 'th',
19084                 cls: 'prev',
19085                 html: '<i class="fa fa-arrow-left"/>'
19086             },
19087             {
19088                 tag: 'th',
19089                 cls: 'switch',
19090                 colspan: '5'
19091             },
19092             {
19093                 tag: 'th',
19094                 cls: 'next',
19095                 html: '<i class="fa fa-arrow-right"/>'
19096             }
19097
19098             ]
19099         }
19100         ]
19101     },
19102     
19103     content : {
19104         tag: 'tbody',
19105         cn: [
19106         {
19107             tag: 'tr',
19108             cn: [
19109             {
19110                 tag: 'td',
19111                 colspan: '7'
19112             }
19113             ]
19114         }
19115         ]
19116     },
19117     
19118     footer : {
19119         tag: 'tfoot',
19120         cn: [
19121         {
19122             tag: 'tr',
19123             cn: [
19124             {
19125                 tag: 'th',
19126                 colspan: '7',
19127                 cls: 'today'
19128             }
19129                     
19130             ]
19131         }
19132         ]
19133     },
19134     
19135     dates:{
19136         en: {
19137             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19138             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19139             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19140             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19141             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19142             today: "Today"
19143         }
19144     },
19145     
19146     modes: [
19147     {
19148         clsName: 'days',
19149         navFnc: 'Month',
19150         navStep: 1
19151     },
19152     {
19153         clsName: 'months',
19154         navFnc: 'FullYear',
19155         navStep: 1
19156     },
19157     {
19158         clsName: 'years',
19159         navFnc: 'FullYear',
19160         navStep: 10
19161     }]
19162 });
19163
19164 Roo.apply(Roo.bootstrap.DateField,  {
19165   
19166     template : {
19167         tag: 'div',
19168         cls: 'datepicker dropdown-menu roo-dynamic',
19169         cn: [
19170         {
19171             tag: 'div',
19172             cls: 'datepicker-days',
19173             cn: [
19174             {
19175                 tag: 'table',
19176                 cls: 'table-condensed',
19177                 cn:[
19178                 Roo.bootstrap.DateField.head,
19179                 {
19180                     tag: 'tbody'
19181                 },
19182                 Roo.bootstrap.DateField.footer
19183                 ]
19184             }
19185             ]
19186         },
19187         {
19188             tag: 'div',
19189             cls: 'datepicker-months',
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             tag: 'div',
19204             cls: 'datepicker-years',
19205             cn: [
19206             {
19207                 tag: 'table',
19208                 cls: 'table-condensed',
19209                 cn:[
19210                 Roo.bootstrap.DateField.head,
19211                 Roo.bootstrap.DateField.content,
19212                 Roo.bootstrap.DateField.footer
19213                 ]
19214             }
19215             ]
19216         }
19217         ]
19218     }
19219 });
19220
19221  
19222
19223  /*
19224  * - LGPL
19225  *
19226  * TimeField
19227  * 
19228  */
19229
19230 /**
19231  * @class Roo.bootstrap.TimeField
19232  * @extends Roo.bootstrap.Input
19233  * Bootstrap DateField class
19234  * 
19235  * 
19236  * @constructor
19237  * Create a new TimeField
19238  * @param {Object} config The config object
19239  */
19240
19241 Roo.bootstrap.TimeField = function(config){
19242     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19243     this.addEvents({
19244             /**
19245              * @event show
19246              * Fires when this field show.
19247              * @param {Roo.bootstrap.DateField} thisthis
19248              * @param {Mixed} date The date value
19249              */
19250             show : true,
19251             /**
19252              * @event show
19253              * Fires when this field hide.
19254              * @param {Roo.bootstrap.DateField} this
19255              * @param {Mixed} date The date value
19256              */
19257             hide : true,
19258             /**
19259              * @event select
19260              * Fires when select a date.
19261              * @param {Roo.bootstrap.DateField} this
19262              * @param {Mixed} date The date value
19263              */
19264             select : true
19265         });
19266 };
19267
19268 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19269     
19270     /**
19271      * @cfg {String} format
19272      * The default time format string which can be overriden for localization support.  The format must be
19273      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19274      */
19275     format : "H:i",
19276        
19277     onRender: function(ct, position)
19278     {
19279         
19280         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19281                 
19282         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19283         
19284         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19285         
19286         this.pop = this.picker().select('>.datepicker-time',true).first();
19287         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19288         
19289         this.picker().on('mousedown', this.onMousedown, this);
19290         this.picker().on('click', this.onClick, this);
19291         
19292         this.picker().addClass('datepicker-dropdown');
19293     
19294         this.fillTime();
19295         this.update();
19296             
19297         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19298         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19299         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19300         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19301         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19302         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19303
19304     },
19305     
19306     fireKey: function(e){
19307         if (!this.picker().isVisible()){
19308             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19309                 this.show();
19310             }
19311             return;
19312         }
19313
19314         e.preventDefault();
19315         
19316         switch(e.keyCode){
19317             case 27: // escape
19318                 this.hide();
19319                 break;
19320             case 37: // left
19321             case 39: // right
19322                 this.onTogglePeriod();
19323                 break;
19324             case 38: // up
19325                 this.onIncrementMinutes();
19326                 break;
19327             case 40: // down
19328                 this.onDecrementMinutes();
19329                 break;
19330             case 13: // enter
19331             case 9: // tab
19332                 this.setTime();
19333                 break;
19334         }
19335     },
19336     
19337     onClick: function(e) {
19338         e.stopPropagation();
19339         e.preventDefault();
19340     },
19341     
19342     picker : function()
19343     {
19344         return this.el.select('.datepicker', true).first();
19345     },
19346     
19347     fillTime: function()
19348     {    
19349         var time = this.pop.select('tbody', true).first();
19350         
19351         time.dom.innerHTML = '';
19352         
19353         time.createChild({
19354             tag: 'tr',
19355             cn: [
19356                 {
19357                     tag: 'td',
19358                     cn: [
19359                         {
19360                             tag: 'a',
19361                             href: '#',
19362                             cls: 'btn',
19363                             cn: [
19364                                 {
19365                                     tag: 'span',
19366                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19367                                 }
19368                             ]
19369                         } 
19370                     ]
19371                 },
19372                 {
19373                     tag: 'td',
19374                     cls: 'separator'
19375                 },
19376                 {
19377                     tag: 'td',
19378                     cn: [
19379                         {
19380                             tag: 'a',
19381                             href: '#',
19382                             cls: 'btn',
19383                             cn: [
19384                                 {
19385                                     tag: 'span',
19386                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19387                                 }
19388                             ]
19389                         }
19390                     ]
19391                 },
19392                 {
19393                     tag: 'td',
19394                     cls: 'separator'
19395                 }
19396             ]
19397         });
19398         
19399         time.createChild({
19400             tag: 'tr',
19401             cn: [
19402                 {
19403                     tag: 'td',
19404                     cn: [
19405                         {
19406                             tag: 'span',
19407                             cls: 'timepicker-hour',
19408                             html: '00'
19409                         }  
19410                     ]
19411                 },
19412                 {
19413                     tag: 'td',
19414                     cls: 'separator',
19415                     html: ':'
19416                 },
19417                 {
19418                     tag: 'td',
19419                     cn: [
19420                         {
19421                             tag: 'span',
19422                             cls: 'timepicker-minute',
19423                             html: '00'
19424                         }  
19425                     ]
19426                 },
19427                 {
19428                     tag: 'td',
19429                     cls: 'separator'
19430                 },
19431                 {
19432                     tag: 'td',
19433                     cn: [
19434                         {
19435                             tag: 'button',
19436                             type: 'button',
19437                             cls: 'btn btn-primary period',
19438                             html: 'AM'
19439                             
19440                         }
19441                     ]
19442                 }
19443             ]
19444         });
19445         
19446         time.createChild({
19447             tag: 'tr',
19448             cn: [
19449                 {
19450                     tag: 'td',
19451                     cn: [
19452                         {
19453                             tag: 'a',
19454                             href: '#',
19455                             cls: 'btn',
19456                             cn: [
19457                                 {
19458                                     tag: 'span',
19459                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19460                                 }
19461                             ]
19462                         }
19463                     ]
19464                 },
19465                 {
19466                     tag: 'td',
19467                     cls: 'separator'
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cn: [
19472                         {
19473                             tag: 'a',
19474                             href: '#',
19475                             cls: 'btn',
19476                             cn: [
19477                                 {
19478                                     tag: 'span',
19479                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19480                                 }
19481                             ]
19482                         }
19483                     ]
19484                 },
19485                 {
19486                     tag: 'td',
19487                     cls: 'separator'
19488                 }
19489             ]
19490         });
19491         
19492     },
19493     
19494     update: function()
19495     {
19496         
19497         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19498         
19499         this.fill();
19500     },
19501     
19502     fill: function() 
19503     {
19504         var hours = this.time.getHours();
19505         var minutes = this.time.getMinutes();
19506         var period = 'AM';
19507         
19508         if(hours > 11){
19509             period = 'PM';
19510         }
19511         
19512         if(hours == 0){
19513             hours = 12;
19514         }
19515         
19516         
19517         if(hours > 12){
19518             hours = hours - 12;
19519         }
19520         
19521         if(hours < 10){
19522             hours = '0' + hours;
19523         }
19524         
19525         if(minutes < 10){
19526             minutes = '0' + minutes;
19527         }
19528         
19529         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19530         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19531         this.pop.select('button', true).first().dom.innerHTML = period;
19532         
19533     },
19534     
19535     place: function()
19536     {   
19537         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19538         
19539         var cls = ['bottom'];
19540         
19541         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19542             cls.pop();
19543             cls.push('top');
19544         }
19545         
19546         cls.push('right');
19547         
19548         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19549             cls.pop();
19550             cls.push('left');
19551         }
19552         
19553         this.picker().addClass(cls.join('-'));
19554         
19555         var _this = this;
19556         
19557         Roo.each(cls, function(c){
19558             if(c == 'bottom'){
19559                 _this.picker().setTop(_this.inputEl().getHeight());
19560                 return;
19561             }
19562             if(c == 'top'){
19563                 _this.picker().setTop(0 - _this.picker().getHeight());
19564                 return;
19565             }
19566             
19567             if(c == 'left'){
19568                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19569                 return;
19570             }
19571             if(c == 'right'){
19572                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19573                 return;
19574             }
19575         });
19576         
19577     },
19578   
19579     onFocus : function()
19580     {
19581         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19582         this.show();
19583     },
19584     
19585     onBlur : function()
19586     {
19587         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19588         this.hide();
19589     },
19590     
19591     show : function()
19592     {
19593         this.picker().show();
19594         this.pop.show();
19595         this.update();
19596         this.place();
19597         
19598         this.fireEvent('show', this, this.date);
19599     },
19600     
19601     hide : function()
19602     {
19603         this.picker().hide();
19604         this.pop.hide();
19605         
19606         this.fireEvent('hide', this, this.date);
19607     },
19608     
19609     setTime : function()
19610     {
19611         this.hide();
19612         this.setValue(this.time.format(this.format));
19613         
19614         this.fireEvent('select', this, this.date);
19615         
19616         
19617     },
19618     
19619     onMousedown: function(e){
19620         e.stopPropagation();
19621         e.preventDefault();
19622     },
19623     
19624     onIncrementHours: function()
19625     {
19626         Roo.log('onIncrementHours');
19627         this.time = this.time.add(Date.HOUR, 1);
19628         this.update();
19629         
19630     },
19631     
19632     onDecrementHours: function()
19633     {
19634         Roo.log('onDecrementHours');
19635         this.time = this.time.add(Date.HOUR, -1);
19636         this.update();
19637     },
19638     
19639     onIncrementMinutes: function()
19640     {
19641         Roo.log('onIncrementMinutes');
19642         this.time = this.time.add(Date.MINUTE, 1);
19643         this.update();
19644     },
19645     
19646     onDecrementMinutes: function()
19647     {
19648         Roo.log('onDecrementMinutes');
19649         this.time = this.time.add(Date.MINUTE, -1);
19650         this.update();
19651     },
19652     
19653     onTogglePeriod: function()
19654     {
19655         Roo.log('onTogglePeriod');
19656         this.time = this.time.add(Date.HOUR, 12);
19657         this.update();
19658     }
19659     
19660    
19661 });
19662
19663 Roo.apply(Roo.bootstrap.TimeField,  {
19664     
19665     content : {
19666         tag: 'tbody',
19667         cn: [
19668             {
19669                 tag: 'tr',
19670                 cn: [
19671                 {
19672                     tag: 'td',
19673                     colspan: '7'
19674                 }
19675                 ]
19676             }
19677         ]
19678     },
19679     
19680     footer : {
19681         tag: 'tfoot',
19682         cn: [
19683             {
19684                 tag: 'tr',
19685                 cn: [
19686                 {
19687                     tag: 'th',
19688                     colspan: '7',
19689                     cls: '',
19690                     cn: [
19691                         {
19692                             tag: 'button',
19693                             cls: 'btn btn-info ok',
19694                             html: 'OK'
19695                         }
19696                     ]
19697                 }
19698
19699                 ]
19700             }
19701         ]
19702     }
19703 });
19704
19705 Roo.apply(Roo.bootstrap.TimeField,  {
19706   
19707     template : {
19708         tag: 'div',
19709         cls: 'datepicker dropdown-menu',
19710         cn: [
19711             {
19712                 tag: 'div',
19713                 cls: 'datepicker-time',
19714                 cn: [
19715                 {
19716                     tag: 'table',
19717                     cls: 'table-condensed',
19718                     cn:[
19719                     Roo.bootstrap.TimeField.content,
19720                     Roo.bootstrap.TimeField.footer
19721                     ]
19722                 }
19723                 ]
19724             }
19725         ]
19726     }
19727 });
19728
19729  
19730
19731  /*
19732  * - LGPL
19733  *
19734  * MonthField
19735  * 
19736  */
19737
19738 /**
19739  * @class Roo.bootstrap.MonthField
19740  * @extends Roo.bootstrap.Input
19741  * Bootstrap MonthField class
19742  * 
19743  * @cfg {String} language default en
19744  * 
19745  * @constructor
19746  * Create a new MonthField
19747  * @param {Object} config The config object
19748  */
19749
19750 Roo.bootstrap.MonthField = function(config){
19751     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19752     
19753     this.addEvents({
19754         /**
19755          * @event show
19756          * Fires when this field show.
19757          * @param {Roo.bootstrap.MonthField} this
19758          * @param {Mixed} date The date value
19759          */
19760         show : true,
19761         /**
19762          * @event show
19763          * Fires when this field hide.
19764          * @param {Roo.bootstrap.MonthField} this
19765          * @param {Mixed} date The date value
19766          */
19767         hide : true,
19768         /**
19769          * @event select
19770          * Fires when select a date.
19771          * @param {Roo.bootstrap.MonthField} this
19772          * @param {String} oldvalue The old value
19773          * @param {String} newvalue The new value
19774          */
19775         select : true
19776     });
19777 };
19778
19779 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19780     
19781     onRender: function(ct, position)
19782     {
19783         
19784         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19785         
19786         this.language = this.language || 'en';
19787         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19788         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19789         
19790         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19791         this.isInline = false;
19792         this.isInput = true;
19793         this.component = this.el.select('.add-on', true).first() || false;
19794         this.component = (this.component && this.component.length === 0) ? false : this.component;
19795         this.hasInput = this.component && this.inputEL().length;
19796         
19797         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19798         
19799         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19800         
19801         this.picker().on('mousedown', this.onMousedown, this);
19802         this.picker().on('click', this.onClick, this);
19803         
19804         this.picker().addClass('datepicker-dropdown');
19805         
19806         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19807             v.setStyle('width', '189px');
19808         });
19809         
19810         this.fillMonths();
19811         
19812         this.update();
19813         
19814         if(this.isInline) {
19815             this.show();
19816         }
19817         
19818     },
19819     
19820     setValue: function(v, suppressEvent)
19821     {   
19822         var o = this.getValue();
19823         
19824         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19825         
19826         this.update();
19827
19828         if(suppressEvent !== true){
19829             this.fireEvent('select', this, o, v);
19830         }
19831         
19832     },
19833     
19834     getValue: function()
19835     {
19836         return this.value;
19837     },
19838     
19839     onClick: function(e) 
19840     {
19841         e.stopPropagation();
19842         e.preventDefault();
19843         
19844         var target = e.getTarget();
19845         
19846         if(target.nodeName.toLowerCase() === 'i'){
19847             target = Roo.get(target).dom.parentNode;
19848         }
19849         
19850         var nodeName = target.nodeName;
19851         var className = target.className;
19852         var html = target.innerHTML;
19853         
19854         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19855             return;
19856         }
19857         
19858         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19859         
19860         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19861         
19862         this.hide();
19863                         
19864     },
19865     
19866     picker : function()
19867     {
19868         return this.pickerEl;
19869     },
19870     
19871     fillMonths: function()
19872     {    
19873         var i = 0;
19874         var months = this.picker().select('>.datepicker-months td', true).first();
19875         
19876         months.dom.innerHTML = '';
19877         
19878         while (i < 12) {
19879             var month = {
19880                 tag: 'span',
19881                 cls: 'month',
19882                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19883             };
19884             
19885             months.createChild(month);
19886         }
19887         
19888     },
19889     
19890     update: function()
19891     {
19892         var _this = this;
19893         
19894         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19895             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19896         }
19897         
19898         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19899             e.removeClass('active');
19900             
19901             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19902                 e.addClass('active');
19903             }
19904         })
19905     },
19906     
19907     place: function()
19908     {
19909         if(this.isInline) {
19910             return;
19911         }
19912         
19913         this.picker().removeClass(['bottom', 'top']);
19914         
19915         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19916             /*
19917              * place to the top of element!
19918              *
19919              */
19920             
19921             this.picker().addClass('top');
19922             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19923             
19924             return;
19925         }
19926         
19927         this.picker().addClass('bottom');
19928         
19929         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19930     },
19931     
19932     onFocus : function()
19933     {
19934         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19935         this.show();
19936     },
19937     
19938     onBlur : function()
19939     {
19940         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19941         
19942         var d = this.inputEl().getValue();
19943         
19944         this.setValue(d);
19945                 
19946         this.hide();
19947     },
19948     
19949     show : function()
19950     {
19951         this.picker().show();
19952         this.picker().select('>.datepicker-months', true).first().show();
19953         this.update();
19954         this.place();
19955         
19956         this.fireEvent('show', this, this.date);
19957     },
19958     
19959     hide : function()
19960     {
19961         if(this.isInline) {
19962             return;
19963         }
19964         this.picker().hide();
19965         this.fireEvent('hide', this, this.date);
19966         
19967     },
19968     
19969     onMousedown: function(e)
19970     {
19971         e.stopPropagation();
19972         e.preventDefault();
19973     },
19974     
19975     keyup: function(e)
19976     {
19977         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19978         this.update();
19979     },
19980
19981     fireKey: function(e)
19982     {
19983         if (!this.picker().isVisible()){
19984             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19985                 this.show();
19986             }
19987             return;
19988         }
19989         
19990         var dir;
19991         
19992         switch(e.keyCode){
19993             case 27: // escape
19994                 this.hide();
19995                 e.preventDefault();
19996                 break;
19997             case 37: // left
19998             case 39: // right
19999                 dir = e.keyCode == 37 ? -1 : 1;
20000                 
20001                 this.vIndex = this.vIndex + dir;
20002                 
20003                 if(this.vIndex < 0){
20004                     this.vIndex = 0;
20005                 }
20006                 
20007                 if(this.vIndex > 11){
20008                     this.vIndex = 11;
20009                 }
20010                 
20011                 if(isNaN(this.vIndex)){
20012                     this.vIndex = 0;
20013                 }
20014                 
20015                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20016                 
20017                 break;
20018             case 38: // up
20019             case 40: // down
20020                 
20021                 dir = e.keyCode == 38 ? -1 : 1;
20022                 
20023                 this.vIndex = this.vIndex + dir * 4;
20024                 
20025                 if(this.vIndex < 0){
20026                     this.vIndex = 0;
20027                 }
20028                 
20029                 if(this.vIndex > 11){
20030                     this.vIndex = 11;
20031                 }
20032                 
20033                 if(isNaN(this.vIndex)){
20034                     this.vIndex = 0;
20035                 }
20036                 
20037                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20038                 break;
20039                 
20040             case 13: // enter
20041                 
20042                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20043                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20044                 }
20045                 
20046                 this.hide();
20047                 e.preventDefault();
20048                 break;
20049             case 9: // tab
20050                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20051                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20052                 }
20053                 this.hide();
20054                 break;
20055             case 16: // shift
20056             case 17: // ctrl
20057             case 18: // alt
20058                 break;
20059             default :
20060                 this.hide();
20061                 
20062         }
20063     },
20064     
20065     remove: function() 
20066     {
20067         this.picker().remove();
20068     }
20069    
20070 });
20071
20072 Roo.apply(Roo.bootstrap.MonthField,  {
20073     
20074     content : {
20075         tag: 'tbody',
20076         cn: [
20077         {
20078             tag: 'tr',
20079             cn: [
20080             {
20081                 tag: 'td',
20082                 colspan: '7'
20083             }
20084             ]
20085         }
20086         ]
20087     },
20088     
20089     dates:{
20090         en: {
20091             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20092             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20093         }
20094     }
20095 });
20096
20097 Roo.apply(Roo.bootstrap.MonthField,  {
20098   
20099     template : {
20100         tag: 'div',
20101         cls: 'datepicker dropdown-menu roo-dynamic',
20102         cn: [
20103             {
20104                 tag: 'div',
20105                 cls: 'datepicker-months',
20106                 cn: [
20107                 {
20108                     tag: 'table',
20109                     cls: 'table-condensed',
20110                     cn:[
20111                         Roo.bootstrap.DateField.content
20112                     ]
20113                 }
20114                 ]
20115             }
20116         ]
20117     }
20118 });
20119
20120  
20121
20122  
20123  /*
20124  * - LGPL
20125  *
20126  * CheckBox
20127  * 
20128  */
20129
20130 /**
20131  * @class Roo.bootstrap.CheckBox
20132  * @extends Roo.bootstrap.Input
20133  * Bootstrap CheckBox class
20134  * 
20135  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20136  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20137  * @cfg {String} boxLabel The text that appears beside the checkbox
20138  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20139  * @cfg {Boolean} checked initnal the element
20140  * @cfg {Boolean} inline inline the element (default false)
20141  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20142  * @cfg {String} tooltip label tooltip
20143  * 
20144  * @constructor
20145  * Create a new CheckBox
20146  * @param {Object} config The config object
20147  */
20148
20149 Roo.bootstrap.CheckBox = function(config){
20150     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20151    
20152     this.addEvents({
20153         /**
20154         * @event check
20155         * Fires when the element is checked or unchecked.
20156         * @param {Roo.bootstrap.CheckBox} this This input
20157         * @param {Boolean} checked The new checked value
20158         */
20159        check : true,
20160        /**
20161         * @event click
20162         * Fires when the element is click.
20163         * @param {Roo.bootstrap.CheckBox} this This input
20164         */
20165        click : true
20166     });
20167     
20168 };
20169
20170 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20171   
20172     inputType: 'checkbox',
20173     inputValue: 1,
20174     valueOff: 0,
20175     boxLabel: false,
20176     checked: false,
20177     weight : false,
20178     inline: false,
20179     tooltip : '',
20180     
20181     getAutoCreate : function()
20182     {
20183         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20184         
20185         var id = Roo.id();
20186         
20187         var cfg = {};
20188         
20189         cfg.cls = 'form-group ' + this.inputType; //input-group
20190         
20191         if(this.inline){
20192             cfg.cls += ' ' + this.inputType + '-inline';
20193         }
20194         
20195         var input =  {
20196             tag: 'input',
20197             id : id,
20198             type : this.inputType,
20199             value : this.inputValue,
20200             cls : 'roo-' + this.inputType, //'form-box',
20201             placeholder : this.placeholder || ''
20202             
20203         };
20204         
20205         if(this.inputType != 'radio'){
20206             var hidden =  {
20207                 tag: 'input',
20208                 type : 'hidden',
20209                 cls : 'roo-hidden-value',
20210                 value : this.checked ? this.inputValue : this.valueOff
20211             };
20212         }
20213         
20214             
20215         if (this.weight) { // Validity check?
20216             cfg.cls += " " + this.inputType + "-" + this.weight;
20217         }
20218         
20219         if (this.disabled) {
20220             input.disabled=true;
20221         }
20222         
20223         if(this.checked){
20224             input.checked = this.checked;
20225         }
20226         
20227         if (this.name) {
20228             
20229             input.name = this.name;
20230             
20231             if(this.inputType != 'radio'){
20232                 hidden.name = this.name;
20233                 input.name = '_hidden_' + this.name;
20234             }
20235         }
20236         
20237         if (this.size) {
20238             input.cls += ' input-' + this.size;
20239         }
20240         
20241         var settings=this;
20242         
20243         ['xs','sm','md','lg'].map(function(size){
20244             if (settings[size]) {
20245                 cfg.cls += ' col-' + size + '-' + settings[size];
20246             }
20247         });
20248         
20249         var inputblock = input;
20250          
20251         if (this.before || this.after) {
20252             
20253             inputblock = {
20254                 cls : 'input-group',
20255                 cn :  [] 
20256             };
20257             
20258             if (this.before) {
20259                 inputblock.cn.push({
20260                     tag :'span',
20261                     cls : 'input-group-addon',
20262                     html : this.before
20263                 });
20264             }
20265             
20266             inputblock.cn.push(input);
20267             
20268             if(this.inputType != 'radio'){
20269                 inputblock.cn.push(hidden);
20270             }
20271             
20272             if (this.after) {
20273                 inputblock.cn.push({
20274                     tag :'span',
20275                     cls : 'input-group-addon',
20276                     html : this.after
20277                 });
20278             }
20279             
20280         }
20281         
20282         if (align ==='left' && this.fieldLabel.length) {
20283 //                Roo.log("left and has label");
20284             cfg.cn = [
20285                 {
20286                     tag: 'label',
20287                     'for' :  id,
20288                     cls : 'control-label',
20289                     html : this.fieldLabel
20290                 },
20291                 {
20292                     cls : "", 
20293                     cn: [
20294                         inputblock
20295                     ]
20296                 }
20297             ];
20298             
20299             if(this.labelWidth > 12){
20300                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20301             }
20302             
20303             if(this.labelWidth < 13 && this.labelmd == 0){
20304                 this.labelmd = this.labelWidth;
20305             }
20306             
20307             if(this.labellg > 0){
20308                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20309                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20310             }
20311             
20312             if(this.labelmd > 0){
20313                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20314                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20315             }
20316             
20317             if(this.labelsm > 0){
20318                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20319                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20320             }
20321             
20322             if(this.labelxs > 0){
20323                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20324                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20325             }
20326             
20327         } else if ( this.fieldLabel.length) {
20328 //                Roo.log(" label");
20329                 cfg.cn = [
20330                    
20331                     {
20332                         tag: this.boxLabel ? 'span' : 'label',
20333                         'for': id,
20334                         cls: 'control-label box-input-label',
20335                         //cls : 'input-group-addon',
20336                         html : this.fieldLabel
20337                     },
20338                     
20339                     inputblock
20340                     
20341                 ];
20342
20343         } else {
20344             
20345 //                Roo.log(" no label && no align");
20346                 cfg.cn = [  inputblock ] ;
20347                 
20348                 
20349         }
20350         
20351         if(this.boxLabel){
20352              var boxLabelCfg = {
20353                 tag: 'label',
20354                 //'for': id, // box label is handled by onclick - so no for...
20355                 cls: 'box-label',
20356                 html: this.boxLabel
20357             };
20358             
20359             if(this.tooltip){
20360                 boxLabelCfg.tooltip = this.tooltip;
20361             }
20362              
20363             cfg.cn.push(boxLabelCfg);
20364         }
20365         
20366         if(this.inputType != 'radio'){
20367             cfg.cn.push(hidden);
20368         }
20369         
20370         return cfg;
20371         
20372     },
20373     
20374     /**
20375      * return the real input element.
20376      */
20377     inputEl: function ()
20378     {
20379         return this.el.select('input.roo-' + this.inputType,true).first();
20380     },
20381     hiddenEl: function ()
20382     {
20383         return this.el.select('input.roo-hidden-value',true).first();
20384     },
20385     
20386     labelEl: function()
20387     {
20388         return this.el.select('label.control-label',true).first();
20389     },
20390     /* depricated... */
20391     
20392     label: function()
20393     {
20394         return this.labelEl();
20395     },
20396     
20397     boxLabelEl: function()
20398     {
20399         return this.el.select('label.box-label',true).first();
20400     },
20401     
20402     initEvents : function()
20403     {
20404 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20405         
20406         this.inputEl().on('click', this.onClick,  this);
20407         
20408         if (this.boxLabel) { 
20409             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20410         }
20411         
20412         this.startValue = this.getValue();
20413         
20414         if(this.groupId){
20415             Roo.bootstrap.CheckBox.register(this);
20416         }
20417     },
20418     
20419     onClick : function(e)
20420     {   
20421         if(this.fireEvent('click', this, e) !== false){
20422             this.setChecked(!this.checked);
20423         }
20424         
20425     },
20426     
20427     setChecked : function(state,suppressEvent)
20428     {
20429         this.startValue = this.getValue();
20430
20431         if(this.inputType == 'radio'){
20432             
20433             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20434                 e.dom.checked = false;
20435             });
20436             
20437             this.inputEl().dom.checked = true;
20438             
20439             this.inputEl().dom.value = this.inputValue;
20440             
20441             if(suppressEvent !== true){
20442                 this.fireEvent('check', this, true);
20443             }
20444             
20445             this.validate();
20446             
20447             return;
20448         }
20449         
20450         this.checked = state;
20451         
20452         this.inputEl().dom.checked = state;
20453         
20454         
20455         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20456         
20457         if(suppressEvent !== true){
20458             this.fireEvent('check', this, state);
20459         }
20460         
20461         this.validate();
20462     },
20463     
20464     getValue : function()
20465     {
20466         if(this.inputType == 'radio'){
20467             return this.getGroupValue();
20468         }
20469         
20470         return this.hiddenEl().dom.value;
20471         
20472     },
20473     
20474     getGroupValue : function()
20475     {
20476         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20477             return '';
20478         }
20479         
20480         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20481     },
20482     
20483     setValue : function(v,suppressEvent)
20484     {
20485         if(this.inputType == 'radio'){
20486             this.setGroupValue(v, suppressEvent);
20487             return;
20488         }
20489         
20490         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20491         
20492         this.validate();
20493     },
20494     
20495     setGroupValue : function(v, suppressEvent)
20496     {
20497         this.startValue = this.getValue();
20498         
20499         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20500             e.dom.checked = false;
20501             
20502             if(e.dom.value == v){
20503                 e.dom.checked = true;
20504             }
20505         });
20506         
20507         if(suppressEvent !== true){
20508             this.fireEvent('check', this, true);
20509         }
20510
20511         this.validate();
20512         
20513         return;
20514     },
20515     
20516     validate : function()
20517     {
20518         if(
20519                 this.disabled || 
20520                 (this.inputType == 'radio' && this.validateRadio()) ||
20521                 (this.inputType == 'checkbox' && this.validateCheckbox())
20522         ){
20523             this.markValid();
20524             return true;
20525         }
20526         
20527         this.markInvalid();
20528         return false;
20529     },
20530     
20531     validateRadio : function()
20532     {
20533         if(this.allowBlank){
20534             return true;
20535         }
20536         
20537         var valid = false;
20538         
20539         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20540             if(!e.dom.checked){
20541                 return;
20542             }
20543             
20544             valid = true;
20545             
20546             return false;
20547         });
20548         
20549         return valid;
20550     },
20551     
20552     validateCheckbox : function()
20553     {
20554         if(!this.groupId){
20555             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20556             //return (this.getValue() == this.inputValue) ? true : false;
20557         }
20558         
20559         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20560         
20561         if(!group){
20562             return false;
20563         }
20564         
20565         var r = false;
20566         
20567         for(var i in group){
20568             if(group[i].el.isVisible(true)){
20569                 r = false;
20570                 break;
20571             }
20572             
20573             r = true;
20574         }
20575         
20576         for(var i in group){
20577             if(r){
20578                 break;
20579             }
20580             
20581             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20582         }
20583         
20584         return r;
20585     },
20586     
20587     /**
20588      * Mark this field as valid
20589      */
20590     markValid : function()
20591     {
20592         var _this = this;
20593         
20594         this.fireEvent('valid', this);
20595         
20596         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20597         
20598         if(this.groupId){
20599             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20600         }
20601         
20602         if(label){
20603             label.markValid();
20604         }
20605
20606         if(this.inputType == 'radio'){
20607             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20608                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20609                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20610             });
20611             
20612             return;
20613         }
20614
20615         if(!this.groupId){
20616             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20617             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20618             return;
20619         }
20620         
20621         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20622         
20623         if(!group){
20624             return;
20625         }
20626         
20627         for(var i in group){
20628             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20629             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20630         }
20631     },
20632     
20633      /**
20634      * Mark this field as invalid
20635      * @param {String} msg The validation message
20636      */
20637     markInvalid : function(msg)
20638     {
20639         if(this.allowBlank){
20640             return;
20641         }
20642         
20643         var _this = this;
20644         
20645         this.fireEvent('invalid', this, msg);
20646         
20647         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20648         
20649         if(this.groupId){
20650             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20651         }
20652         
20653         if(label){
20654             label.markInvalid();
20655         }
20656             
20657         if(this.inputType == 'radio'){
20658             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20659                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20660                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20661             });
20662             
20663             return;
20664         }
20665         
20666         if(!this.groupId){
20667             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20668             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20669             return;
20670         }
20671         
20672         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20673         
20674         if(!group){
20675             return;
20676         }
20677         
20678         for(var i in group){
20679             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20680             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20681         }
20682         
20683     },
20684     
20685     clearInvalid : function()
20686     {
20687         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20688         
20689         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20690         
20691         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20692         
20693         if (label && label.iconEl) {
20694             label.iconEl.removeClass(label.validClass);
20695             label.iconEl.removeClass(label.invalidClass);
20696         }
20697     },
20698     
20699     disable : function()
20700     {
20701         if(this.inputType != 'radio'){
20702             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20703             return;
20704         }
20705         
20706         var _this = this;
20707         
20708         if(this.rendered){
20709             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20710                 _this.getActionEl().addClass(this.disabledClass);
20711                 e.dom.disabled = true;
20712             });
20713         }
20714         
20715         this.disabled = true;
20716         this.fireEvent("disable", this);
20717         return this;
20718     },
20719
20720     enable : function()
20721     {
20722         if(this.inputType != 'radio'){
20723             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20724             return;
20725         }
20726         
20727         var _this = this;
20728         
20729         if(this.rendered){
20730             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20731                 _this.getActionEl().removeClass(this.disabledClass);
20732                 e.dom.disabled = false;
20733             });
20734         }
20735         
20736         this.disabled = false;
20737         this.fireEvent("enable", this);
20738         return this;
20739     }
20740
20741 });
20742
20743 Roo.apply(Roo.bootstrap.CheckBox, {
20744     
20745     groups: {},
20746     
20747      /**
20748     * register a CheckBox Group
20749     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20750     */
20751     register : function(checkbox)
20752     {
20753         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20754             this.groups[checkbox.groupId] = {};
20755         }
20756         
20757         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20758             return;
20759         }
20760         
20761         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20762         
20763     },
20764     /**
20765     * fetch a CheckBox Group based on the group ID
20766     * @param {string} the group ID
20767     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20768     */
20769     get: function(groupId) {
20770         if (typeof(this.groups[groupId]) == 'undefined') {
20771             return false;
20772         }
20773         
20774         return this.groups[groupId] ;
20775     }
20776     
20777     
20778 });
20779 /*
20780  * - LGPL
20781  *
20782  * RadioItem
20783  * 
20784  */
20785
20786 /**
20787  * @class Roo.bootstrap.Radio
20788  * @extends Roo.bootstrap.Component
20789  * Bootstrap Radio class
20790  * @cfg {String} boxLabel - the label associated
20791  * @cfg {String} value - the value of radio
20792  * 
20793  * @constructor
20794  * Create a new Radio
20795  * @param {Object} config The config object
20796  */
20797 Roo.bootstrap.Radio = function(config){
20798     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20799     
20800 };
20801
20802 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20803     
20804     boxLabel : '',
20805     
20806     value : '',
20807     
20808     getAutoCreate : function()
20809     {
20810         var cfg = {
20811             tag : 'div',
20812             cls : 'form-group radio',
20813             cn : [
20814                 {
20815                     tag : 'label',
20816                     cls : 'box-label',
20817                     html : this.boxLabel
20818                 }
20819             ]
20820         };
20821         
20822         return cfg;
20823     },
20824     
20825     initEvents : function() 
20826     {
20827         this.parent().register(this);
20828         
20829         this.el.on('click', this.onClick, this);
20830         
20831     },
20832     
20833     onClick : function()
20834     {
20835         this.setChecked(true);
20836     },
20837     
20838     setChecked : function(state, suppressEvent)
20839     {
20840         this.parent().setValue(this.value, suppressEvent);
20841         
20842     },
20843     
20844     setBoxLabel : function(v)
20845     {
20846         this.boxLabel = v;
20847         
20848         if(this.rendered){
20849             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20850         }
20851     }
20852     
20853 });
20854  
20855
20856  /*
20857  * - LGPL
20858  *
20859  * Input
20860  * 
20861  */
20862
20863 /**
20864  * @class Roo.bootstrap.SecurePass
20865  * @extends Roo.bootstrap.Input
20866  * Bootstrap SecurePass class
20867  *
20868  * 
20869  * @constructor
20870  * Create a new SecurePass
20871  * @param {Object} config The config object
20872  */
20873  
20874 Roo.bootstrap.SecurePass = function (config) {
20875     // these go here, so the translation tool can replace them..
20876     this.errors = {
20877         PwdEmpty: "Please type a password, and then retype it to confirm.",
20878         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20879         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20880         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20881         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20882         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20883         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20884         TooWeak: "Your password is Too Weak."
20885     },
20886     this.meterLabel = "Password strength:";
20887     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20888     this.meterClass = [
20889         "roo-password-meter-tooweak", 
20890         "roo-password-meter-weak", 
20891         "roo-password-meter-medium", 
20892         "roo-password-meter-strong", 
20893         "roo-password-meter-grey"
20894     ];
20895     
20896     this.errors = {};
20897     
20898     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20899 }
20900
20901 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20902     /**
20903      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20904      * {
20905      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20906      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20907      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20908      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20909      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20910      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20911      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20912      * })
20913      */
20914     // private
20915     
20916     meterWidth: 300,
20917     errorMsg :'',    
20918     errors: false,
20919     imageRoot: '/',
20920     /**
20921      * @cfg {String/Object} Label for the strength meter (defaults to
20922      * 'Password strength:')
20923      */
20924     // private
20925     meterLabel: '',
20926     /**
20927      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20928      * ['Weak', 'Medium', 'Strong'])
20929      */
20930     // private    
20931     pwdStrengths: false,    
20932     // private
20933     strength: 0,
20934     // private
20935     _lastPwd: null,
20936     // private
20937     kCapitalLetter: 0,
20938     kSmallLetter: 1,
20939     kDigit: 2,
20940     kPunctuation: 3,
20941     
20942     insecure: false,
20943     // private
20944     initEvents: function ()
20945     {
20946         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20947
20948         if (this.el.is('input[type=password]') && Roo.isSafari) {
20949             this.el.on('keydown', this.SafariOnKeyDown, this);
20950         }
20951
20952         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20953     },
20954     // private
20955     onRender: function (ct, position)
20956     {
20957         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20958         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20959         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20960
20961         this.trigger.createChild({
20962                    cn: [
20963                     {
20964                     //id: 'PwdMeter',
20965                     tag: 'div',
20966                     cls: 'roo-password-meter-grey col-xs-12',
20967                     style: {
20968                         //width: 0,
20969                         //width: this.meterWidth + 'px'                                                
20970                         }
20971                     },
20972                     {                            
20973                          cls: 'roo-password-meter-text'                          
20974                     }
20975                 ]            
20976         });
20977
20978          
20979         if (this.hideTrigger) {
20980             this.trigger.setDisplayed(false);
20981         }
20982         this.setSize(this.width || '', this.height || '');
20983     },
20984     // private
20985     onDestroy: function ()
20986     {
20987         if (this.trigger) {
20988             this.trigger.removeAllListeners();
20989             this.trigger.remove();
20990         }
20991         if (this.wrap) {
20992             this.wrap.remove();
20993         }
20994         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20995     },
20996     // private
20997     checkStrength: function ()
20998     {
20999         var pwd = this.inputEl().getValue();
21000         if (pwd == this._lastPwd) {
21001             return;
21002         }
21003
21004         var strength;
21005         if (this.ClientSideStrongPassword(pwd)) {
21006             strength = 3;
21007         } else if (this.ClientSideMediumPassword(pwd)) {
21008             strength = 2;
21009         } else if (this.ClientSideWeakPassword(pwd)) {
21010             strength = 1;
21011         } else {
21012             strength = 0;
21013         }
21014         
21015         Roo.log('strength1: ' + strength);
21016         
21017         //var pm = this.trigger.child('div/div/div').dom;
21018         var pm = this.trigger.child('div/div');
21019         pm.removeClass(this.meterClass);
21020         pm.addClass(this.meterClass[strength]);
21021                 
21022         
21023         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21024                 
21025         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21026         
21027         this._lastPwd = pwd;
21028     },
21029     reset: function ()
21030     {
21031         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21032         
21033         this._lastPwd = '';
21034         
21035         var pm = this.trigger.child('div/div');
21036         pm.removeClass(this.meterClass);
21037         pm.addClass('roo-password-meter-grey');        
21038         
21039         
21040         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21041         
21042         pt.innerHTML = '';
21043         this.inputEl().dom.type='password';
21044     },
21045     // private
21046     validateValue: function (value)
21047     {
21048         
21049         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21050             return false;
21051         }
21052         if (value.length == 0) {
21053             if (this.allowBlank) {
21054                 this.clearInvalid();
21055                 return true;
21056             }
21057
21058             this.markInvalid(this.errors.PwdEmpty);
21059             this.errorMsg = this.errors.PwdEmpty;
21060             return false;
21061         }
21062         
21063         if(this.insecure){
21064             return true;
21065         }
21066         
21067         if ('[\x21-\x7e]*'.match(value)) {
21068             this.markInvalid(this.errors.PwdBadChar);
21069             this.errorMsg = this.errors.PwdBadChar;
21070             return false;
21071         }
21072         if (value.length < 6) {
21073             this.markInvalid(this.errors.PwdShort);
21074             this.errorMsg = this.errors.PwdShort;
21075             return false;
21076         }
21077         if (value.length > 16) {
21078             this.markInvalid(this.errors.PwdLong);
21079             this.errorMsg = this.errors.PwdLong;
21080             return false;
21081         }
21082         var strength;
21083         if (this.ClientSideStrongPassword(value)) {
21084             strength = 3;
21085         } else if (this.ClientSideMediumPassword(value)) {
21086             strength = 2;
21087         } else if (this.ClientSideWeakPassword(value)) {
21088             strength = 1;
21089         } else {
21090             strength = 0;
21091         }
21092
21093         
21094         if (strength < 2) {
21095             //this.markInvalid(this.errors.TooWeak);
21096             this.errorMsg = this.errors.TooWeak;
21097             //return false;
21098         }
21099         
21100         
21101         console.log('strength2: ' + strength);
21102         
21103         //var pm = this.trigger.child('div/div/div').dom;
21104         
21105         var pm = this.trigger.child('div/div');
21106         pm.removeClass(this.meterClass);
21107         pm.addClass(this.meterClass[strength]);
21108                 
21109         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21110                 
21111         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21112         
21113         this.errorMsg = ''; 
21114         return true;
21115     },
21116     // private
21117     CharacterSetChecks: function (type)
21118     {
21119         this.type = type;
21120         this.fResult = false;
21121     },
21122     // private
21123     isctype: function (character, type)
21124     {
21125         switch (type) {  
21126             case this.kCapitalLetter:
21127                 if (character >= 'A' && character <= 'Z') {
21128                     return true;
21129                 }
21130                 break;
21131             
21132             case this.kSmallLetter:
21133                 if (character >= 'a' && character <= 'z') {
21134                     return true;
21135                 }
21136                 break;
21137             
21138             case this.kDigit:
21139                 if (character >= '0' && character <= '9') {
21140                     return true;
21141                 }
21142                 break;
21143             
21144             case this.kPunctuation:
21145                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21146                     return true;
21147                 }
21148                 break;
21149             
21150             default:
21151                 return false;
21152         }
21153
21154     },
21155     // private
21156     IsLongEnough: function (pwd, size)
21157     {
21158         return !(pwd == null || isNaN(size) || pwd.length < size);
21159     },
21160     // private
21161     SpansEnoughCharacterSets: function (word, nb)
21162     {
21163         if (!this.IsLongEnough(word, nb))
21164         {
21165             return false;
21166         }
21167
21168         var characterSetChecks = new Array(
21169             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21170             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21171         );
21172         
21173         for (var index = 0; index < word.length; ++index) {
21174             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21175                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21176                     characterSetChecks[nCharSet].fResult = true;
21177                     break;
21178                 }
21179             }
21180         }
21181
21182         var nCharSets = 0;
21183         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21184             if (characterSetChecks[nCharSet].fResult) {
21185                 ++nCharSets;
21186             }
21187         }
21188
21189         if (nCharSets < nb) {
21190             return false;
21191         }
21192         return true;
21193     },
21194     // private
21195     ClientSideStrongPassword: function (pwd)
21196     {
21197         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21198     },
21199     // private
21200     ClientSideMediumPassword: function (pwd)
21201     {
21202         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21203     },
21204     // private
21205     ClientSideWeakPassword: function (pwd)
21206     {
21207         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21208     }
21209           
21210 })//<script type="text/javascript">
21211
21212 /*
21213  * Based  Ext JS Library 1.1.1
21214  * Copyright(c) 2006-2007, Ext JS, LLC.
21215  * LGPL
21216  *
21217  */
21218  
21219 /**
21220  * @class Roo.HtmlEditorCore
21221  * @extends Roo.Component
21222  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21223  *
21224  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21225  */
21226
21227 Roo.HtmlEditorCore = function(config){
21228     
21229     
21230     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21231     
21232     
21233     this.addEvents({
21234         /**
21235          * @event initialize
21236          * Fires when the editor is fully initialized (including the iframe)
21237          * @param {Roo.HtmlEditorCore} this
21238          */
21239         initialize: true,
21240         /**
21241          * @event activate
21242          * Fires when the editor is first receives the focus. Any insertion must wait
21243          * until after this event.
21244          * @param {Roo.HtmlEditorCore} this
21245          */
21246         activate: true,
21247          /**
21248          * @event beforesync
21249          * Fires before the textarea is updated with content from the editor iframe. Return false
21250          * to cancel the sync.
21251          * @param {Roo.HtmlEditorCore} this
21252          * @param {String} html
21253          */
21254         beforesync: true,
21255          /**
21256          * @event beforepush
21257          * Fires before the iframe editor is updated with content from the textarea. Return false
21258          * to cancel the push.
21259          * @param {Roo.HtmlEditorCore} this
21260          * @param {String} html
21261          */
21262         beforepush: true,
21263          /**
21264          * @event sync
21265          * Fires when the textarea is updated with content from the editor iframe.
21266          * @param {Roo.HtmlEditorCore} this
21267          * @param {String} html
21268          */
21269         sync: true,
21270          /**
21271          * @event push
21272          * Fires when the iframe editor is updated with content from the textarea.
21273          * @param {Roo.HtmlEditorCore} this
21274          * @param {String} html
21275          */
21276         push: true,
21277         
21278         /**
21279          * @event editorevent
21280          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21281          * @param {Roo.HtmlEditorCore} this
21282          */
21283         editorevent: true
21284         
21285     });
21286     
21287     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21288     
21289     // defaults : white / black...
21290     this.applyBlacklists();
21291     
21292     
21293     
21294 };
21295
21296
21297 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21298
21299
21300      /**
21301      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21302      */
21303     
21304     owner : false,
21305     
21306      /**
21307      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21308      *                        Roo.resizable.
21309      */
21310     resizable : false,
21311      /**
21312      * @cfg {Number} height (in pixels)
21313      */   
21314     height: 300,
21315    /**
21316      * @cfg {Number} width (in pixels)
21317      */   
21318     width: 500,
21319     
21320     /**
21321      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21322      * 
21323      */
21324     stylesheets: false,
21325     
21326     // id of frame..
21327     frameId: false,
21328     
21329     // private properties
21330     validationEvent : false,
21331     deferHeight: true,
21332     initialized : false,
21333     activated : false,
21334     sourceEditMode : false,
21335     onFocus : Roo.emptyFn,
21336     iframePad:3,
21337     hideMode:'offsets',
21338     
21339     clearUp: true,
21340     
21341     // blacklist + whitelisted elements..
21342     black: false,
21343     white: false,
21344      
21345     bodyCls : '',
21346
21347     /**
21348      * Protected method that will not generally be called directly. It
21349      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21350      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21351      */
21352     getDocMarkup : function(){
21353         // body styles..
21354         var st = '';
21355         
21356         // inherit styels from page...?? 
21357         if (this.stylesheets === false) {
21358             
21359             Roo.get(document.head).select('style').each(function(node) {
21360                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21361             });
21362             
21363             Roo.get(document.head).select('link').each(function(node) { 
21364                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21365             });
21366             
21367         } else if (!this.stylesheets.length) {
21368                 // simple..
21369                 st = '<style type="text/css">' +
21370                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21371                    '</style>';
21372         } else { 
21373             st = '<style type="text/css">' +
21374                     this.stylesheets +
21375                 '</style>';
21376         }
21377         
21378         st +=  '<style type="text/css">' +
21379             'IMG { cursor: pointer } ' +
21380         '</style>';
21381
21382         var cls = 'roo-htmleditor-body';
21383         
21384         if(this.bodyCls.length){
21385             cls += ' ' + this.bodyCls;
21386         }
21387         
21388         return '<html><head>' + st  +
21389             //<style type="text/css">' +
21390             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21391             //'</style>' +
21392             ' </head><body class="' +  cls + '"></body></html>';
21393     },
21394
21395     // private
21396     onRender : function(ct, position)
21397     {
21398         var _t = this;
21399         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21400         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21401         
21402         
21403         this.el.dom.style.border = '0 none';
21404         this.el.dom.setAttribute('tabIndex', -1);
21405         this.el.addClass('x-hidden hide');
21406         
21407         
21408         
21409         if(Roo.isIE){ // fix IE 1px bogus margin
21410             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21411         }
21412        
21413         
21414         this.frameId = Roo.id();
21415         
21416          
21417         
21418         var iframe = this.owner.wrap.createChild({
21419             tag: 'iframe',
21420             cls: 'form-control', // bootstrap..
21421             id: this.frameId,
21422             name: this.frameId,
21423             frameBorder : 'no',
21424             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21425         }, this.el
21426         );
21427         
21428         
21429         this.iframe = iframe.dom;
21430
21431          this.assignDocWin();
21432         
21433         this.doc.designMode = 'on';
21434        
21435         this.doc.open();
21436         this.doc.write(this.getDocMarkup());
21437         this.doc.close();
21438
21439         
21440         var task = { // must defer to wait for browser to be ready
21441             run : function(){
21442                 //console.log("run task?" + this.doc.readyState);
21443                 this.assignDocWin();
21444                 if(this.doc.body || this.doc.readyState == 'complete'){
21445                     try {
21446                         this.doc.designMode="on";
21447                     } catch (e) {
21448                         return;
21449                     }
21450                     Roo.TaskMgr.stop(task);
21451                     this.initEditor.defer(10, this);
21452                 }
21453             },
21454             interval : 10,
21455             duration: 10000,
21456             scope: this
21457         };
21458         Roo.TaskMgr.start(task);
21459
21460     },
21461
21462     // private
21463     onResize : function(w, h)
21464     {
21465          Roo.log('resize: ' +w + ',' + h );
21466         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21467         if(!this.iframe){
21468             return;
21469         }
21470         if(typeof w == 'number'){
21471             
21472             this.iframe.style.width = w + 'px';
21473         }
21474         if(typeof h == 'number'){
21475             
21476             this.iframe.style.height = h + 'px';
21477             if(this.doc){
21478                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21479             }
21480         }
21481         
21482     },
21483
21484     /**
21485      * Toggles the editor between standard and source edit mode.
21486      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21487      */
21488     toggleSourceEdit : function(sourceEditMode){
21489         
21490         this.sourceEditMode = sourceEditMode === true;
21491         
21492         if(this.sourceEditMode){
21493  
21494             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21495             
21496         }else{
21497             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21498             //this.iframe.className = '';
21499             this.deferFocus();
21500         }
21501         //this.setSize(this.owner.wrap.getSize());
21502         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21503     },
21504
21505     
21506   
21507
21508     /**
21509      * Protected method that will not generally be called directly. If you need/want
21510      * custom HTML cleanup, this is the method you should override.
21511      * @param {String} html The HTML to be cleaned
21512      * return {String} The cleaned HTML
21513      */
21514     cleanHtml : function(html){
21515         html = String(html);
21516         if(html.length > 5){
21517             if(Roo.isSafari){ // strip safari nonsense
21518                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21519             }
21520         }
21521         if(html == '&nbsp;'){
21522             html = '';
21523         }
21524         return html;
21525     },
21526
21527     /**
21528      * HTML Editor -> Textarea
21529      * Protected method that will not generally be called directly. Syncs the contents
21530      * of the editor iframe with the textarea.
21531      */
21532     syncValue : function(){
21533         if(this.initialized){
21534             var bd = (this.doc.body || this.doc.documentElement);
21535             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21536             var html = bd.innerHTML;
21537             if(Roo.isSafari){
21538                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21539                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21540                 if(m && m[1]){
21541                     html = '<div style="'+m[0]+'">' + html + '</div>';
21542                 }
21543             }
21544             html = this.cleanHtml(html);
21545             // fix up the special chars.. normaly like back quotes in word...
21546             // however we do not want to do this with chinese..
21547             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21548                 var cc = b.charCodeAt();
21549                 if (
21550                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21551                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21552                     (cc >= 0xf900 && cc < 0xfb00 )
21553                 ) {
21554                         return b;
21555                 }
21556                 return "&#"+cc+";" 
21557             });
21558             if(this.owner.fireEvent('beforesync', this, html) !== false){
21559                 this.el.dom.value = html;
21560                 this.owner.fireEvent('sync', this, html);
21561             }
21562         }
21563     },
21564
21565     /**
21566      * Protected method that will not generally be called directly. Pushes the value of the textarea
21567      * into the iframe editor.
21568      */
21569     pushValue : function(){
21570         if(this.initialized){
21571             var v = this.el.dom.value.trim();
21572             
21573 //            if(v.length < 1){
21574 //                v = '&#160;';
21575 //            }
21576             
21577             if(this.owner.fireEvent('beforepush', this, v) !== false){
21578                 var d = (this.doc.body || this.doc.documentElement);
21579                 d.innerHTML = v;
21580                 this.cleanUpPaste();
21581                 this.el.dom.value = d.innerHTML;
21582                 this.owner.fireEvent('push', this, v);
21583             }
21584         }
21585     },
21586
21587     // private
21588     deferFocus : function(){
21589         this.focus.defer(10, this);
21590     },
21591
21592     // doc'ed in Field
21593     focus : function(){
21594         if(this.win && !this.sourceEditMode){
21595             this.win.focus();
21596         }else{
21597             this.el.focus();
21598         }
21599     },
21600     
21601     assignDocWin: function()
21602     {
21603         var iframe = this.iframe;
21604         
21605          if(Roo.isIE){
21606             this.doc = iframe.contentWindow.document;
21607             this.win = iframe.contentWindow;
21608         } else {
21609 //            if (!Roo.get(this.frameId)) {
21610 //                return;
21611 //            }
21612 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21613 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21614             
21615             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21616                 return;
21617             }
21618             
21619             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21620             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21621         }
21622     },
21623     
21624     // private
21625     initEditor : function(){
21626         //console.log("INIT EDITOR");
21627         this.assignDocWin();
21628         
21629         
21630         
21631         this.doc.designMode="on";
21632         this.doc.open();
21633         this.doc.write(this.getDocMarkup());
21634         this.doc.close();
21635         
21636         var dbody = (this.doc.body || this.doc.documentElement);
21637         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21638         // this copies styles from the containing element into thsi one..
21639         // not sure why we need all of this..
21640         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21641         
21642         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21643         //ss['background-attachment'] = 'fixed'; // w3c
21644         dbody.bgProperties = 'fixed'; // ie
21645         //Roo.DomHelper.applyStyles(dbody, ss);
21646         Roo.EventManager.on(this.doc, {
21647             //'mousedown': this.onEditorEvent,
21648             'mouseup': this.onEditorEvent,
21649             'dblclick': this.onEditorEvent,
21650             'click': this.onEditorEvent,
21651             'keyup': this.onEditorEvent,
21652             buffer:100,
21653             scope: this
21654         });
21655         if(Roo.isGecko){
21656             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21657         }
21658         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21659             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21660         }
21661         this.initialized = true;
21662
21663         this.owner.fireEvent('initialize', this);
21664         this.pushValue();
21665     },
21666
21667     // private
21668     onDestroy : function(){
21669         
21670         
21671         
21672         if(this.rendered){
21673             
21674             //for (var i =0; i < this.toolbars.length;i++) {
21675             //    // fixme - ask toolbars for heights?
21676             //    this.toolbars[i].onDestroy();
21677            // }
21678             
21679             //this.wrap.dom.innerHTML = '';
21680             //this.wrap.remove();
21681         }
21682     },
21683
21684     // private
21685     onFirstFocus : function(){
21686         
21687         this.assignDocWin();
21688         
21689         
21690         this.activated = true;
21691          
21692     
21693         if(Roo.isGecko){ // prevent silly gecko errors
21694             this.win.focus();
21695             var s = this.win.getSelection();
21696             if(!s.focusNode || s.focusNode.nodeType != 3){
21697                 var r = s.getRangeAt(0);
21698                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21699                 r.collapse(true);
21700                 this.deferFocus();
21701             }
21702             try{
21703                 this.execCmd('useCSS', true);
21704                 this.execCmd('styleWithCSS', false);
21705             }catch(e){}
21706         }
21707         this.owner.fireEvent('activate', this);
21708     },
21709
21710     // private
21711     adjustFont: function(btn){
21712         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21713         //if(Roo.isSafari){ // safari
21714         //    adjust *= 2;
21715        // }
21716         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21717         if(Roo.isSafari){ // safari
21718             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21719             v =  (v < 10) ? 10 : v;
21720             v =  (v > 48) ? 48 : v;
21721             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21722             
21723         }
21724         
21725         
21726         v = Math.max(1, v+adjust);
21727         
21728         this.execCmd('FontSize', v  );
21729     },
21730
21731     onEditorEvent : function(e)
21732     {
21733         this.owner.fireEvent('editorevent', this, e);
21734       //  this.updateToolbar();
21735         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21736     },
21737
21738     insertTag : function(tg)
21739     {
21740         // could be a bit smarter... -> wrap the current selected tRoo..
21741         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21742             
21743             range = this.createRange(this.getSelection());
21744             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21745             wrappingNode.appendChild(range.extractContents());
21746             range.insertNode(wrappingNode);
21747
21748             return;
21749             
21750             
21751             
21752         }
21753         this.execCmd("formatblock",   tg);
21754         
21755     },
21756     
21757     insertText : function(txt)
21758     {
21759         
21760         
21761         var range = this.createRange();
21762         range.deleteContents();
21763                //alert(Sender.getAttribute('label'));
21764                
21765         range.insertNode(this.doc.createTextNode(txt));
21766     } ,
21767     
21768      
21769
21770     /**
21771      * Executes a Midas editor command on the editor document and performs necessary focus and
21772      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21773      * @param {String} cmd The Midas command
21774      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21775      */
21776     relayCmd : function(cmd, value){
21777         this.win.focus();
21778         this.execCmd(cmd, value);
21779         this.owner.fireEvent('editorevent', this);
21780         //this.updateToolbar();
21781         this.owner.deferFocus();
21782     },
21783
21784     /**
21785      * Executes a Midas editor command directly on the editor document.
21786      * For visual commands, you should use {@link #relayCmd} instead.
21787      * <b>This should only be called after the editor is initialized.</b>
21788      * @param {String} cmd The Midas command
21789      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21790      */
21791     execCmd : function(cmd, value){
21792         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21793         this.syncValue();
21794     },
21795  
21796  
21797    
21798     /**
21799      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21800      * to insert tRoo.
21801      * @param {String} text | dom node.. 
21802      */
21803     insertAtCursor : function(text)
21804     {
21805         
21806         if(!this.activated){
21807             return;
21808         }
21809         /*
21810         if(Roo.isIE){
21811             this.win.focus();
21812             var r = this.doc.selection.createRange();
21813             if(r){
21814                 r.collapse(true);
21815                 r.pasteHTML(text);
21816                 this.syncValue();
21817                 this.deferFocus();
21818             
21819             }
21820             return;
21821         }
21822         */
21823         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21824             this.win.focus();
21825             
21826             
21827             // from jquery ui (MIT licenced)
21828             var range, node;
21829             var win = this.win;
21830             
21831             if (win.getSelection && win.getSelection().getRangeAt) {
21832                 range = win.getSelection().getRangeAt(0);
21833                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21834                 range.insertNode(node);
21835             } else if (win.document.selection && win.document.selection.createRange) {
21836                 // no firefox support
21837                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21838                 win.document.selection.createRange().pasteHTML(txt);
21839             } else {
21840                 // no firefox support
21841                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21842                 this.execCmd('InsertHTML', txt);
21843             } 
21844             
21845             this.syncValue();
21846             
21847             this.deferFocus();
21848         }
21849     },
21850  // private
21851     mozKeyPress : function(e){
21852         if(e.ctrlKey){
21853             var c = e.getCharCode(), cmd;
21854           
21855             if(c > 0){
21856                 c = String.fromCharCode(c).toLowerCase();
21857                 switch(c){
21858                     case 'b':
21859                         cmd = 'bold';
21860                         break;
21861                     case 'i':
21862                         cmd = 'italic';
21863                         break;
21864                     
21865                     case 'u':
21866                         cmd = 'underline';
21867                         break;
21868                     
21869                     case 'v':
21870                         this.cleanUpPaste.defer(100, this);
21871                         return;
21872                         
21873                 }
21874                 if(cmd){
21875                     this.win.focus();
21876                     this.execCmd(cmd);
21877                     this.deferFocus();
21878                     e.preventDefault();
21879                 }
21880                 
21881             }
21882         }
21883     },
21884
21885     // private
21886     fixKeys : function(){ // load time branching for fastest keydown performance
21887         if(Roo.isIE){
21888             return function(e){
21889                 var k = e.getKey(), r;
21890                 if(k == e.TAB){
21891                     e.stopEvent();
21892                     r = this.doc.selection.createRange();
21893                     if(r){
21894                         r.collapse(true);
21895                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21896                         this.deferFocus();
21897                     }
21898                     return;
21899                 }
21900                 
21901                 if(k == e.ENTER){
21902                     r = this.doc.selection.createRange();
21903                     if(r){
21904                         var target = r.parentElement();
21905                         if(!target || target.tagName.toLowerCase() != 'li'){
21906                             e.stopEvent();
21907                             r.pasteHTML('<br />');
21908                             r.collapse(false);
21909                             r.select();
21910                         }
21911                     }
21912                 }
21913                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21914                     this.cleanUpPaste.defer(100, this);
21915                     return;
21916                 }
21917                 
21918                 
21919             };
21920         }else if(Roo.isOpera){
21921             return function(e){
21922                 var k = e.getKey();
21923                 if(k == e.TAB){
21924                     e.stopEvent();
21925                     this.win.focus();
21926                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21927                     this.deferFocus();
21928                 }
21929                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21930                     this.cleanUpPaste.defer(100, this);
21931                     return;
21932                 }
21933                 
21934             };
21935         }else if(Roo.isSafari){
21936             return function(e){
21937                 var k = e.getKey();
21938                 
21939                 if(k == e.TAB){
21940                     e.stopEvent();
21941                     this.execCmd('InsertText','\t');
21942                     this.deferFocus();
21943                     return;
21944                 }
21945                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21946                     this.cleanUpPaste.defer(100, this);
21947                     return;
21948                 }
21949                 
21950              };
21951         }
21952     }(),
21953     
21954     getAllAncestors: function()
21955     {
21956         var p = this.getSelectedNode();
21957         var a = [];
21958         if (!p) {
21959             a.push(p); // push blank onto stack..
21960             p = this.getParentElement();
21961         }
21962         
21963         
21964         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21965             a.push(p);
21966             p = p.parentNode;
21967         }
21968         a.push(this.doc.body);
21969         return a;
21970     },
21971     lastSel : false,
21972     lastSelNode : false,
21973     
21974     
21975     getSelection : function() 
21976     {
21977         this.assignDocWin();
21978         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21979     },
21980     
21981     getSelectedNode: function() 
21982     {
21983         // this may only work on Gecko!!!
21984         
21985         // should we cache this!!!!
21986         
21987         
21988         
21989          
21990         var range = this.createRange(this.getSelection()).cloneRange();
21991         
21992         if (Roo.isIE) {
21993             var parent = range.parentElement();
21994             while (true) {
21995                 var testRange = range.duplicate();
21996                 testRange.moveToElementText(parent);
21997                 if (testRange.inRange(range)) {
21998                     break;
21999                 }
22000                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22001                     break;
22002                 }
22003                 parent = parent.parentElement;
22004             }
22005             return parent;
22006         }
22007         
22008         // is ancestor a text element.
22009         var ac =  range.commonAncestorContainer;
22010         if (ac.nodeType == 3) {
22011             ac = ac.parentNode;
22012         }
22013         
22014         var ar = ac.childNodes;
22015          
22016         var nodes = [];
22017         var other_nodes = [];
22018         var has_other_nodes = false;
22019         for (var i=0;i<ar.length;i++) {
22020             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22021                 continue;
22022             }
22023             // fullly contained node.
22024             
22025             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22026                 nodes.push(ar[i]);
22027                 continue;
22028             }
22029             
22030             // probably selected..
22031             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22032                 other_nodes.push(ar[i]);
22033                 continue;
22034             }
22035             // outer..
22036             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22037                 continue;
22038             }
22039             
22040             
22041             has_other_nodes = true;
22042         }
22043         if (!nodes.length && other_nodes.length) {
22044             nodes= other_nodes;
22045         }
22046         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22047             return false;
22048         }
22049         
22050         return nodes[0];
22051     },
22052     createRange: function(sel)
22053     {
22054         // this has strange effects when using with 
22055         // top toolbar - not sure if it's a great idea.
22056         //this.editor.contentWindow.focus();
22057         if (typeof sel != "undefined") {
22058             try {
22059                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22060             } catch(e) {
22061                 return this.doc.createRange();
22062             }
22063         } else {
22064             return this.doc.createRange();
22065         }
22066     },
22067     getParentElement: function()
22068     {
22069         
22070         this.assignDocWin();
22071         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22072         
22073         var range = this.createRange(sel);
22074          
22075         try {
22076             var p = range.commonAncestorContainer;
22077             while (p.nodeType == 3) { // text node
22078                 p = p.parentNode;
22079             }
22080             return p;
22081         } catch (e) {
22082             return null;
22083         }
22084     
22085     },
22086     /***
22087      *
22088      * Range intersection.. the hard stuff...
22089      *  '-1' = before
22090      *  '0' = hits..
22091      *  '1' = after.
22092      *         [ -- selected range --- ]
22093      *   [fail]                        [fail]
22094      *
22095      *    basically..
22096      *      if end is before start or  hits it. fail.
22097      *      if start is after end or hits it fail.
22098      *
22099      *   if either hits (but other is outside. - then it's not 
22100      *   
22101      *    
22102      **/
22103     
22104     
22105     // @see http://www.thismuchiknow.co.uk/?p=64.
22106     rangeIntersectsNode : function(range, node)
22107     {
22108         var nodeRange = node.ownerDocument.createRange();
22109         try {
22110             nodeRange.selectNode(node);
22111         } catch (e) {
22112             nodeRange.selectNodeContents(node);
22113         }
22114     
22115         var rangeStartRange = range.cloneRange();
22116         rangeStartRange.collapse(true);
22117     
22118         var rangeEndRange = range.cloneRange();
22119         rangeEndRange.collapse(false);
22120     
22121         var nodeStartRange = nodeRange.cloneRange();
22122         nodeStartRange.collapse(true);
22123     
22124         var nodeEndRange = nodeRange.cloneRange();
22125         nodeEndRange.collapse(false);
22126     
22127         return rangeStartRange.compareBoundaryPoints(
22128                  Range.START_TO_START, nodeEndRange) == -1 &&
22129                rangeEndRange.compareBoundaryPoints(
22130                  Range.START_TO_START, nodeStartRange) == 1;
22131         
22132          
22133     },
22134     rangeCompareNode : function(range, node)
22135     {
22136         var nodeRange = node.ownerDocument.createRange();
22137         try {
22138             nodeRange.selectNode(node);
22139         } catch (e) {
22140             nodeRange.selectNodeContents(node);
22141         }
22142         
22143         
22144         range.collapse(true);
22145     
22146         nodeRange.collapse(true);
22147      
22148         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22149         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22150          
22151         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22152         
22153         var nodeIsBefore   =  ss == 1;
22154         var nodeIsAfter    = ee == -1;
22155         
22156         if (nodeIsBefore && nodeIsAfter) {
22157             return 0; // outer
22158         }
22159         if (!nodeIsBefore && nodeIsAfter) {
22160             return 1; //right trailed.
22161         }
22162         
22163         if (nodeIsBefore && !nodeIsAfter) {
22164             return 2;  // left trailed.
22165         }
22166         // fully contined.
22167         return 3;
22168     },
22169
22170     // private? - in a new class?
22171     cleanUpPaste :  function()
22172     {
22173         // cleans up the whole document..
22174         Roo.log('cleanuppaste');
22175         
22176         this.cleanUpChildren(this.doc.body);
22177         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22178         if (clean != this.doc.body.innerHTML) {
22179             this.doc.body.innerHTML = clean;
22180         }
22181         
22182     },
22183     
22184     cleanWordChars : function(input) {// change the chars to hex code
22185         var he = Roo.HtmlEditorCore;
22186         
22187         var output = input;
22188         Roo.each(he.swapCodes, function(sw) { 
22189             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22190             
22191             output = output.replace(swapper, sw[1]);
22192         });
22193         
22194         return output;
22195     },
22196     
22197     
22198     cleanUpChildren : function (n)
22199     {
22200         if (!n.childNodes.length) {
22201             return;
22202         }
22203         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22204            this.cleanUpChild(n.childNodes[i]);
22205         }
22206     },
22207     
22208     
22209         
22210     
22211     cleanUpChild : function (node)
22212     {
22213         var ed = this;
22214         //console.log(node);
22215         if (node.nodeName == "#text") {
22216             // clean up silly Windows -- stuff?
22217             return; 
22218         }
22219         if (node.nodeName == "#comment") {
22220             node.parentNode.removeChild(node);
22221             // clean up silly Windows -- stuff?
22222             return; 
22223         }
22224         var lcname = node.tagName.toLowerCase();
22225         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22226         // whitelist of tags..
22227         
22228         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22229             // remove node.
22230             node.parentNode.removeChild(node);
22231             return;
22232             
22233         }
22234         
22235         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22236         
22237         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22238         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22239         
22240         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22241         //    remove_keep_children = true;
22242         //}
22243         
22244         if (remove_keep_children) {
22245             this.cleanUpChildren(node);
22246             // inserts everything just before this node...
22247             while (node.childNodes.length) {
22248                 var cn = node.childNodes[0];
22249                 node.removeChild(cn);
22250                 node.parentNode.insertBefore(cn, node);
22251             }
22252             node.parentNode.removeChild(node);
22253             return;
22254         }
22255         
22256         if (!node.attributes || !node.attributes.length) {
22257             this.cleanUpChildren(node);
22258             return;
22259         }
22260         
22261         function cleanAttr(n,v)
22262         {
22263             
22264             if (v.match(/^\./) || v.match(/^\//)) {
22265                 return;
22266             }
22267             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22268                 return;
22269             }
22270             if (v.match(/^#/)) {
22271                 return;
22272             }
22273 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22274             node.removeAttribute(n);
22275             
22276         }
22277         
22278         var cwhite = this.cwhite;
22279         var cblack = this.cblack;
22280             
22281         function cleanStyle(n,v)
22282         {
22283             if (v.match(/expression/)) { //XSS?? should we even bother..
22284                 node.removeAttribute(n);
22285                 return;
22286             }
22287             
22288             var parts = v.split(/;/);
22289             var clean = [];
22290             
22291             Roo.each(parts, function(p) {
22292                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22293                 if (!p.length) {
22294                     return true;
22295                 }
22296                 var l = p.split(':').shift().replace(/\s+/g,'');
22297                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22298                 
22299                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22300 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22301                     //node.removeAttribute(n);
22302                     return true;
22303                 }
22304                 //Roo.log()
22305                 // only allow 'c whitelisted system attributes'
22306                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22307 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22308                     //node.removeAttribute(n);
22309                     return true;
22310                 }
22311                 
22312                 
22313                  
22314                 
22315                 clean.push(p);
22316                 return true;
22317             });
22318             if (clean.length) { 
22319                 node.setAttribute(n, clean.join(';'));
22320             } else {
22321                 node.removeAttribute(n);
22322             }
22323             
22324         }
22325         
22326         
22327         for (var i = node.attributes.length-1; i > -1 ; i--) {
22328             var a = node.attributes[i];
22329             //console.log(a);
22330             
22331             if (a.name.toLowerCase().substr(0,2)=='on')  {
22332                 node.removeAttribute(a.name);
22333                 continue;
22334             }
22335             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22336                 node.removeAttribute(a.name);
22337                 continue;
22338             }
22339             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22340                 cleanAttr(a.name,a.value); // fixme..
22341                 continue;
22342             }
22343             if (a.name == 'style') {
22344                 cleanStyle(a.name,a.value);
22345                 continue;
22346             }
22347             /// clean up MS crap..
22348             // tecnically this should be a list of valid class'es..
22349             
22350             
22351             if (a.name == 'class') {
22352                 if (a.value.match(/^Mso/)) {
22353                     node.className = '';
22354                 }
22355                 
22356                 if (a.value.match(/^body$/)) {
22357                     node.className = '';
22358                 }
22359                 continue;
22360             }
22361             
22362             // style cleanup!?
22363             // class cleanup?
22364             
22365         }
22366         
22367         
22368         this.cleanUpChildren(node);
22369         
22370         
22371     },
22372     
22373     /**
22374      * Clean up MS wordisms...
22375      */
22376     cleanWord : function(node)
22377     {
22378         
22379         
22380         if (!node) {
22381             this.cleanWord(this.doc.body);
22382             return;
22383         }
22384         if (node.nodeName == "#text") {
22385             // clean up silly Windows -- stuff?
22386             return; 
22387         }
22388         if (node.nodeName == "#comment") {
22389             node.parentNode.removeChild(node);
22390             // clean up silly Windows -- stuff?
22391             return; 
22392         }
22393         
22394         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22395             node.parentNode.removeChild(node);
22396             return;
22397         }
22398         
22399         // remove - but keep children..
22400         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22401             while (node.childNodes.length) {
22402                 var cn = node.childNodes[0];
22403                 node.removeChild(cn);
22404                 node.parentNode.insertBefore(cn, node);
22405             }
22406             node.parentNode.removeChild(node);
22407             this.iterateChildren(node, this.cleanWord);
22408             return;
22409         }
22410         // clean styles
22411         if (node.className.length) {
22412             
22413             var cn = node.className.split(/\W+/);
22414             var cna = [];
22415             Roo.each(cn, function(cls) {
22416                 if (cls.match(/Mso[a-zA-Z]+/)) {
22417                     return;
22418                 }
22419                 cna.push(cls);
22420             });
22421             node.className = cna.length ? cna.join(' ') : '';
22422             if (!cna.length) {
22423                 node.removeAttribute("class");
22424             }
22425         }
22426         
22427         if (node.hasAttribute("lang")) {
22428             node.removeAttribute("lang");
22429         }
22430         
22431         if (node.hasAttribute("style")) {
22432             
22433             var styles = node.getAttribute("style").split(";");
22434             var nstyle = [];
22435             Roo.each(styles, function(s) {
22436                 if (!s.match(/:/)) {
22437                     return;
22438                 }
22439                 var kv = s.split(":");
22440                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22441                     return;
22442                 }
22443                 // what ever is left... we allow.
22444                 nstyle.push(s);
22445             });
22446             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22447             if (!nstyle.length) {
22448                 node.removeAttribute('style');
22449             }
22450         }
22451         this.iterateChildren(node, this.cleanWord);
22452         
22453         
22454         
22455     },
22456     /**
22457      * iterateChildren of a Node, calling fn each time, using this as the scole..
22458      * @param {DomNode} node node to iterate children of.
22459      * @param {Function} fn method of this class to call on each item.
22460      */
22461     iterateChildren : function(node, fn)
22462     {
22463         if (!node.childNodes.length) {
22464                 return;
22465         }
22466         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22467            fn.call(this, node.childNodes[i])
22468         }
22469     },
22470     
22471     
22472     /**
22473      * cleanTableWidths.
22474      *
22475      * Quite often pasting from word etc.. results in tables with column and widths.
22476      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22477      *
22478      */
22479     cleanTableWidths : function(node)
22480     {
22481          
22482          
22483         if (!node) {
22484             this.cleanTableWidths(this.doc.body);
22485             return;
22486         }
22487         
22488         // ignore list...
22489         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22490             return; 
22491         }
22492         Roo.log(node.tagName);
22493         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22494             this.iterateChildren(node, this.cleanTableWidths);
22495             return;
22496         }
22497         if (node.hasAttribute('width')) {
22498             node.removeAttribute('width');
22499         }
22500         
22501          
22502         if (node.hasAttribute("style")) {
22503             // pretty basic...
22504             
22505             var styles = node.getAttribute("style").split(";");
22506             var nstyle = [];
22507             Roo.each(styles, function(s) {
22508                 if (!s.match(/:/)) {
22509                     return;
22510                 }
22511                 var kv = s.split(":");
22512                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22513                     return;
22514                 }
22515                 // what ever is left... we allow.
22516                 nstyle.push(s);
22517             });
22518             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22519             if (!nstyle.length) {
22520                 node.removeAttribute('style');
22521             }
22522         }
22523         
22524         this.iterateChildren(node, this.cleanTableWidths);
22525         
22526         
22527     },
22528     
22529     
22530     
22531     
22532     domToHTML : function(currentElement, depth, nopadtext) {
22533         
22534         depth = depth || 0;
22535         nopadtext = nopadtext || false;
22536     
22537         if (!currentElement) {
22538             return this.domToHTML(this.doc.body);
22539         }
22540         
22541         //Roo.log(currentElement);
22542         var j;
22543         var allText = false;
22544         var nodeName = currentElement.nodeName;
22545         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22546         
22547         if  (nodeName == '#text') {
22548             
22549             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22550         }
22551         
22552         
22553         var ret = '';
22554         if (nodeName != 'BODY') {
22555              
22556             var i = 0;
22557             // Prints the node tagName, such as <A>, <IMG>, etc
22558             if (tagName) {
22559                 var attr = [];
22560                 for(i = 0; i < currentElement.attributes.length;i++) {
22561                     // quoting?
22562                     var aname = currentElement.attributes.item(i).name;
22563                     if (!currentElement.attributes.item(i).value.length) {
22564                         continue;
22565                     }
22566                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22567                 }
22568                 
22569                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22570             } 
22571             else {
22572                 
22573                 // eack
22574             }
22575         } else {
22576             tagName = false;
22577         }
22578         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22579             return ret;
22580         }
22581         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22582             nopadtext = true;
22583         }
22584         
22585         
22586         // Traverse the tree
22587         i = 0;
22588         var currentElementChild = currentElement.childNodes.item(i);
22589         var allText = true;
22590         var innerHTML  = '';
22591         lastnode = '';
22592         while (currentElementChild) {
22593             // Formatting code (indent the tree so it looks nice on the screen)
22594             var nopad = nopadtext;
22595             if (lastnode == 'SPAN') {
22596                 nopad  = true;
22597             }
22598             // text
22599             if  (currentElementChild.nodeName == '#text') {
22600                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22601                 toadd = nopadtext ? toadd : toadd.trim();
22602                 if (!nopad && toadd.length > 80) {
22603                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22604                 }
22605                 innerHTML  += toadd;
22606                 
22607                 i++;
22608                 currentElementChild = currentElement.childNodes.item(i);
22609                 lastNode = '';
22610                 continue;
22611             }
22612             allText = false;
22613             
22614             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22615                 
22616             // Recursively traverse the tree structure of the child node
22617             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22618             lastnode = currentElementChild.nodeName;
22619             i++;
22620             currentElementChild=currentElement.childNodes.item(i);
22621         }
22622         
22623         ret += innerHTML;
22624         
22625         if (!allText) {
22626                 // The remaining code is mostly for formatting the tree
22627             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22628         }
22629         
22630         
22631         if (tagName) {
22632             ret+= "</"+tagName+">";
22633         }
22634         return ret;
22635         
22636     },
22637         
22638     applyBlacklists : function()
22639     {
22640         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22641         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22642         
22643         this.white = [];
22644         this.black = [];
22645         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22646             if (b.indexOf(tag) > -1) {
22647                 return;
22648             }
22649             this.white.push(tag);
22650             
22651         }, this);
22652         
22653         Roo.each(w, function(tag) {
22654             if (b.indexOf(tag) > -1) {
22655                 return;
22656             }
22657             if (this.white.indexOf(tag) > -1) {
22658                 return;
22659             }
22660             this.white.push(tag);
22661             
22662         }, this);
22663         
22664         
22665         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22666             if (w.indexOf(tag) > -1) {
22667                 return;
22668             }
22669             this.black.push(tag);
22670             
22671         }, this);
22672         
22673         Roo.each(b, function(tag) {
22674             if (w.indexOf(tag) > -1) {
22675                 return;
22676             }
22677             if (this.black.indexOf(tag) > -1) {
22678                 return;
22679             }
22680             this.black.push(tag);
22681             
22682         }, this);
22683         
22684         
22685         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22686         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22687         
22688         this.cwhite = [];
22689         this.cblack = [];
22690         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22691             if (b.indexOf(tag) > -1) {
22692                 return;
22693             }
22694             this.cwhite.push(tag);
22695             
22696         }, this);
22697         
22698         Roo.each(w, function(tag) {
22699             if (b.indexOf(tag) > -1) {
22700                 return;
22701             }
22702             if (this.cwhite.indexOf(tag) > -1) {
22703                 return;
22704             }
22705             this.cwhite.push(tag);
22706             
22707         }, this);
22708         
22709         
22710         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22711             if (w.indexOf(tag) > -1) {
22712                 return;
22713             }
22714             this.cblack.push(tag);
22715             
22716         }, this);
22717         
22718         Roo.each(b, function(tag) {
22719             if (w.indexOf(tag) > -1) {
22720                 return;
22721             }
22722             if (this.cblack.indexOf(tag) > -1) {
22723                 return;
22724             }
22725             this.cblack.push(tag);
22726             
22727         }, this);
22728     },
22729     
22730     setStylesheets : function(stylesheets)
22731     {
22732         if(typeof(stylesheets) == 'string'){
22733             Roo.get(this.iframe.contentDocument.head).createChild({
22734                 tag : 'link',
22735                 rel : 'stylesheet',
22736                 type : 'text/css',
22737                 href : stylesheets
22738             });
22739             
22740             return;
22741         }
22742         var _this = this;
22743      
22744         Roo.each(stylesheets, function(s) {
22745             if(!s.length){
22746                 return;
22747             }
22748             
22749             Roo.get(_this.iframe.contentDocument.head).createChild({
22750                 tag : 'link',
22751                 rel : 'stylesheet',
22752                 type : 'text/css',
22753                 href : s
22754             });
22755         });
22756
22757         
22758     },
22759     
22760     removeStylesheets : function()
22761     {
22762         var _this = this;
22763         
22764         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22765             s.remove();
22766         });
22767     },
22768     
22769     setStyle : function(style)
22770     {
22771         Roo.get(this.iframe.contentDocument.head).createChild({
22772             tag : 'style',
22773             type : 'text/css',
22774             html : style
22775         });
22776
22777         return;
22778     }
22779     
22780     // hide stuff that is not compatible
22781     /**
22782      * @event blur
22783      * @hide
22784      */
22785     /**
22786      * @event change
22787      * @hide
22788      */
22789     /**
22790      * @event focus
22791      * @hide
22792      */
22793     /**
22794      * @event specialkey
22795      * @hide
22796      */
22797     /**
22798      * @cfg {String} fieldClass @hide
22799      */
22800     /**
22801      * @cfg {String} focusClass @hide
22802      */
22803     /**
22804      * @cfg {String} autoCreate @hide
22805      */
22806     /**
22807      * @cfg {String} inputType @hide
22808      */
22809     /**
22810      * @cfg {String} invalidClass @hide
22811      */
22812     /**
22813      * @cfg {String} invalidText @hide
22814      */
22815     /**
22816      * @cfg {String} msgFx @hide
22817      */
22818     /**
22819      * @cfg {String} validateOnBlur @hide
22820      */
22821 });
22822
22823 Roo.HtmlEditorCore.white = [
22824         'area', 'br', 'img', 'input', 'hr', 'wbr',
22825         
22826        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22827        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22828        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22829        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22830        'table',   'ul',         'xmp', 
22831        
22832        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22833       'thead',   'tr', 
22834      
22835       'dir', 'menu', 'ol', 'ul', 'dl',
22836        
22837       'embed',  'object'
22838 ];
22839
22840
22841 Roo.HtmlEditorCore.black = [
22842     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22843         'applet', // 
22844         'base',   'basefont', 'bgsound', 'blink',  'body', 
22845         'frame',  'frameset', 'head',    'html',   'ilayer', 
22846         'iframe', 'layer',  'link',     'meta',    'object',   
22847         'script', 'style' ,'title',  'xml' // clean later..
22848 ];
22849 Roo.HtmlEditorCore.clean = [
22850     'script', 'style', 'title', 'xml'
22851 ];
22852 Roo.HtmlEditorCore.remove = [
22853     'font'
22854 ];
22855 // attributes..
22856
22857 Roo.HtmlEditorCore.ablack = [
22858     'on'
22859 ];
22860     
22861 Roo.HtmlEditorCore.aclean = [ 
22862     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22863 ];
22864
22865 // protocols..
22866 Roo.HtmlEditorCore.pwhite= [
22867         'http',  'https',  'mailto'
22868 ];
22869
22870 // white listed style attributes.
22871 Roo.HtmlEditorCore.cwhite= [
22872       //  'text-align', /// default is to allow most things..
22873       
22874          
22875 //        'font-size'//??
22876 ];
22877
22878 // black listed style attributes.
22879 Roo.HtmlEditorCore.cblack= [
22880       //  'font-size' -- this can be set by the project 
22881 ];
22882
22883
22884 Roo.HtmlEditorCore.swapCodes   =[ 
22885     [    8211, "--" ], 
22886     [    8212, "--" ], 
22887     [    8216,  "'" ],  
22888     [    8217, "'" ],  
22889     [    8220, '"' ],  
22890     [    8221, '"' ],  
22891     [    8226, "*" ],  
22892     [    8230, "..." ]
22893 ]; 
22894
22895     /*
22896  * - LGPL
22897  *
22898  * HtmlEditor
22899  * 
22900  */
22901
22902 /**
22903  * @class Roo.bootstrap.HtmlEditor
22904  * @extends Roo.bootstrap.TextArea
22905  * Bootstrap HtmlEditor class
22906
22907  * @constructor
22908  * Create a new HtmlEditor
22909  * @param {Object} config The config object
22910  */
22911
22912 Roo.bootstrap.HtmlEditor = function(config){
22913     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22914     if (!this.toolbars) {
22915         this.toolbars = [];
22916     }
22917     
22918     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22919     this.addEvents({
22920             /**
22921              * @event initialize
22922              * Fires when the editor is fully initialized (including the iframe)
22923              * @param {HtmlEditor} this
22924              */
22925             initialize: true,
22926             /**
22927              * @event activate
22928              * Fires when the editor is first receives the focus. Any insertion must wait
22929              * until after this event.
22930              * @param {HtmlEditor} this
22931              */
22932             activate: true,
22933              /**
22934              * @event beforesync
22935              * Fires before the textarea is updated with content from the editor iframe. Return false
22936              * to cancel the sync.
22937              * @param {HtmlEditor} this
22938              * @param {String} html
22939              */
22940             beforesync: true,
22941              /**
22942              * @event beforepush
22943              * Fires before the iframe editor is updated with content from the textarea. Return false
22944              * to cancel the push.
22945              * @param {HtmlEditor} this
22946              * @param {String} html
22947              */
22948             beforepush: true,
22949              /**
22950              * @event sync
22951              * Fires when the textarea is updated with content from the editor iframe.
22952              * @param {HtmlEditor} this
22953              * @param {String} html
22954              */
22955             sync: true,
22956              /**
22957              * @event push
22958              * Fires when the iframe editor is updated with content from the textarea.
22959              * @param {HtmlEditor} this
22960              * @param {String} html
22961              */
22962             push: true,
22963              /**
22964              * @event editmodechange
22965              * Fires when the editor switches edit modes
22966              * @param {HtmlEditor} this
22967              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22968              */
22969             editmodechange: true,
22970             /**
22971              * @event editorevent
22972              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22973              * @param {HtmlEditor} this
22974              */
22975             editorevent: true,
22976             /**
22977              * @event firstfocus
22978              * Fires when on first focus - needed by toolbars..
22979              * @param {HtmlEditor} this
22980              */
22981             firstfocus: true,
22982             /**
22983              * @event autosave
22984              * Auto save the htmlEditor value as a file into Events
22985              * @param {HtmlEditor} this
22986              */
22987             autosave: true,
22988             /**
22989              * @event savedpreview
22990              * preview the saved version of htmlEditor
22991              * @param {HtmlEditor} this
22992              */
22993             savedpreview: true
22994         });
22995 };
22996
22997
22998 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22999     
23000     
23001       /**
23002      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23003      */
23004     toolbars : false,
23005     
23006      /**
23007     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23008     */
23009     btns : [],
23010    
23011      /**
23012      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23013      *                        Roo.resizable.
23014      */
23015     resizable : false,
23016      /**
23017      * @cfg {Number} height (in pixels)
23018      */   
23019     height: 300,
23020    /**
23021      * @cfg {Number} width (in pixels)
23022      */   
23023     width: false,
23024     
23025     /**
23026      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23027      * 
23028      */
23029     stylesheets: false,
23030     
23031     // id of frame..
23032     frameId: false,
23033     
23034     // private properties
23035     validationEvent : false,
23036     deferHeight: true,
23037     initialized : false,
23038     activated : false,
23039     
23040     onFocus : Roo.emptyFn,
23041     iframePad:3,
23042     hideMode:'offsets',
23043     
23044     tbContainer : false,
23045     
23046     bodyCls : '',
23047     
23048     toolbarContainer :function() {
23049         return this.wrap.select('.x-html-editor-tb',true).first();
23050     },
23051
23052     /**
23053      * Protected method that will not generally be called directly. It
23054      * is called when the editor creates its toolbar. Override this method if you need to
23055      * add custom toolbar buttons.
23056      * @param {HtmlEditor} editor
23057      */
23058     createToolbar : function(){
23059         Roo.log('renewing');
23060         Roo.log("create toolbars");
23061         
23062         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23063         this.toolbars[0].render(this.toolbarContainer());
23064         
23065         return;
23066         
23067 //        if (!editor.toolbars || !editor.toolbars.length) {
23068 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23069 //        }
23070 //        
23071 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23072 //            editor.toolbars[i] = Roo.factory(
23073 //                    typeof(editor.toolbars[i]) == 'string' ?
23074 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23075 //                Roo.bootstrap.HtmlEditor);
23076 //            editor.toolbars[i].init(editor);
23077 //        }
23078     },
23079
23080      
23081     // private
23082     onRender : function(ct, position)
23083     {
23084        // Roo.log("Call onRender: " + this.xtype);
23085         var _t = this;
23086         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23087       
23088         this.wrap = this.inputEl().wrap({
23089             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23090         });
23091         
23092         this.editorcore.onRender(ct, position);
23093          
23094         if (this.resizable) {
23095             this.resizeEl = new Roo.Resizable(this.wrap, {
23096                 pinned : true,
23097                 wrap: true,
23098                 dynamic : true,
23099                 minHeight : this.height,
23100                 height: this.height,
23101                 handles : this.resizable,
23102                 width: this.width,
23103                 listeners : {
23104                     resize : function(r, w, h) {
23105                         _t.onResize(w,h); // -something
23106                     }
23107                 }
23108             });
23109             
23110         }
23111         this.createToolbar(this);
23112        
23113         
23114         if(!this.width && this.resizable){
23115             this.setSize(this.wrap.getSize());
23116         }
23117         if (this.resizeEl) {
23118             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23119             // should trigger onReize..
23120         }
23121         
23122     },
23123
23124     // private
23125     onResize : function(w, h)
23126     {
23127         Roo.log('resize: ' +w + ',' + h );
23128         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23129         var ew = false;
23130         var eh = false;
23131         
23132         if(this.inputEl() ){
23133             if(typeof w == 'number'){
23134                 var aw = w - this.wrap.getFrameWidth('lr');
23135                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23136                 ew = aw;
23137             }
23138             if(typeof h == 'number'){
23139                  var tbh = -11;  // fixme it needs to tool bar size!
23140                 for (var i =0; i < this.toolbars.length;i++) {
23141                     // fixme - ask toolbars for heights?
23142                     tbh += this.toolbars[i].el.getHeight();
23143                     //if (this.toolbars[i].footer) {
23144                     //    tbh += this.toolbars[i].footer.el.getHeight();
23145                     //}
23146                 }
23147               
23148                 
23149                 
23150                 
23151                 
23152                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23153                 ah -= 5; // knock a few pixes off for look..
23154                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23155                 var eh = ah;
23156             }
23157         }
23158         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23159         this.editorcore.onResize(ew,eh);
23160         
23161     },
23162
23163     /**
23164      * Toggles the editor between standard and source edit mode.
23165      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23166      */
23167     toggleSourceEdit : function(sourceEditMode)
23168     {
23169         this.editorcore.toggleSourceEdit(sourceEditMode);
23170         
23171         if(this.editorcore.sourceEditMode){
23172             Roo.log('editor - showing textarea');
23173             
23174 //            Roo.log('in');
23175 //            Roo.log(this.syncValue());
23176             this.syncValue();
23177             this.inputEl().removeClass(['hide', 'x-hidden']);
23178             this.inputEl().dom.removeAttribute('tabIndex');
23179             this.inputEl().focus();
23180         }else{
23181             Roo.log('editor - hiding textarea');
23182 //            Roo.log('out')
23183 //            Roo.log(this.pushValue()); 
23184             this.pushValue();
23185             
23186             this.inputEl().addClass(['hide', 'x-hidden']);
23187             this.inputEl().dom.setAttribute('tabIndex', -1);
23188             //this.deferFocus();
23189         }
23190          
23191         if(this.resizable){
23192             this.setSize(this.wrap.getSize());
23193         }
23194         
23195         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23196     },
23197  
23198     // private (for BoxComponent)
23199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23200
23201     // private (for BoxComponent)
23202     getResizeEl : function(){
23203         return this.wrap;
23204     },
23205
23206     // private (for BoxComponent)
23207     getPositionEl : function(){
23208         return this.wrap;
23209     },
23210
23211     // private
23212     initEvents : function(){
23213         this.originalValue = this.getValue();
23214     },
23215
23216 //    /**
23217 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23218 //     * @method
23219 //     */
23220 //    markInvalid : Roo.emptyFn,
23221 //    /**
23222 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23223 //     * @method
23224 //     */
23225 //    clearInvalid : Roo.emptyFn,
23226
23227     setValue : function(v){
23228         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23229         this.editorcore.pushValue();
23230     },
23231
23232      
23233     // private
23234     deferFocus : function(){
23235         this.focus.defer(10, this);
23236     },
23237
23238     // doc'ed in Field
23239     focus : function(){
23240         this.editorcore.focus();
23241         
23242     },
23243       
23244
23245     // private
23246     onDestroy : function(){
23247         
23248         
23249         
23250         if(this.rendered){
23251             
23252             for (var i =0; i < this.toolbars.length;i++) {
23253                 // fixme - ask toolbars for heights?
23254                 this.toolbars[i].onDestroy();
23255             }
23256             
23257             this.wrap.dom.innerHTML = '';
23258             this.wrap.remove();
23259         }
23260     },
23261
23262     // private
23263     onFirstFocus : function(){
23264         //Roo.log("onFirstFocus");
23265         this.editorcore.onFirstFocus();
23266          for (var i =0; i < this.toolbars.length;i++) {
23267             this.toolbars[i].onFirstFocus();
23268         }
23269         
23270     },
23271     
23272     // private
23273     syncValue : function()
23274     {   
23275         this.editorcore.syncValue();
23276     },
23277     
23278     pushValue : function()
23279     {   
23280         this.editorcore.pushValue();
23281     }
23282      
23283     
23284     // hide stuff that is not compatible
23285     /**
23286      * @event blur
23287      * @hide
23288      */
23289     /**
23290      * @event change
23291      * @hide
23292      */
23293     /**
23294      * @event focus
23295      * @hide
23296      */
23297     /**
23298      * @event specialkey
23299      * @hide
23300      */
23301     /**
23302      * @cfg {String} fieldClass @hide
23303      */
23304     /**
23305      * @cfg {String} focusClass @hide
23306      */
23307     /**
23308      * @cfg {String} autoCreate @hide
23309      */
23310     /**
23311      * @cfg {String} inputType @hide
23312      */
23313     /**
23314      * @cfg {String} invalidClass @hide
23315      */
23316     /**
23317      * @cfg {String} invalidText @hide
23318      */
23319     /**
23320      * @cfg {String} msgFx @hide
23321      */
23322     /**
23323      * @cfg {String} validateOnBlur @hide
23324      */
23325 });
23326  
23327     
23328    
23329    
23330    
23331       
23332 Roo.namespace('Roo.bootstrap.htmleditor');
23333 /**
23334  * @class Roo.bootstrap.HtmlEditorToolbar1
23335  * Basic Toolbar
23336  * 
23337  * Usage:
23338  *
23339  new Roo.bootstrap.HtmlEditor({
23340     ....
23341     toolbars : [
23342         new Roo.bootstrap.HtmlEditorToolbar1({
23343             disable : { fonts: 1 , format: 1, ..., ... , ...],
23344             btns : [ .... ]
23345         })
23346     }
23347      
23348  * 
23349  * @cfg {Object} disable List of elements to disable..
23350  * @cfg {Array} btns List of additional buttons.
23351  * 
23352  * 
23353  * NEEDS Extra CSS? 
23354  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23355  */
23356  
23357 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23358 {
23359     
23360     Roo.apply(this, config);
23361     
23362     // default disabled, based on 'good practice'..
23363     this.disable = this.disable || {};
23364     Roo.applyIf(this.disable, {
23365         fontSize : true,
23366         colors : true,
23367         specialElements : true
23368     });
23369     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23370     
23371     this.editor = config.editor;
23372     this.editorcore = config.editor.editorcore;
23373     
23374     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23375     
23376     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23377     // dont call parent... till later.
23378 }
23379 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23380      
23381     bar : true,
23382     
23383     editor : false,
23384     editorcore : false,
23385     
23386     
23387     formats : [
23388         "p" ,  
23389         "h1","h2","h3","h4","h5","h6", 
23390         "pre", "code", 
23391         "abbr", "acronym", "address", "cite", "samp", "var",
23392         'div','span'
23393     ],
23394     
23395     onRender : function(ct, position)
23396     {
23397        // Roo.log("Call onRender: " + this.xtype);
23398         
23399        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23400        Roo.log(this.el);
23401        this.el.dom.style.marginBottom = '0';
23402        var _this = this;
23403        var editorcore = this.editorcore;
23404        var editor= this.editor;
23405        
23406        var children = [];
23407        var btn = function(id,cmd , toggle, handler, html){
23408        
23409             var  event = toggle ? 'toggle' : 'click';
23410        
23411             var a = {
23412                 size : 'sm',
23413                 xtype: 'Button',
23414                 xns: Roo.bootstrap,
23415                 glyphicon : id,
23416                 cmd : id || cmd,
23417                 enableToggle:toggle !== false,
23418                 html : html || '',
23419                 pressed : toggle ? false : null,
23420                 listeners : {}
23421             };
23422             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23423                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23424             };
23425             children.push(a);
23426             return a;
23427        }
23428        
23429     //    var cb_box = function...
23430         
23431         var style = {
23432                 xtype: 'Button',
23433                 size : 'sm',
23434                 xns: Roo.bootstrap,
23435                 glyphicon : 'font',
23436                 //html : 'submit'
23437                 menu : {
23438                     xtype: 'Menu',
23439                     xns: Roo.bootstrap,
23440                     items:  []
23441                 }
23442         };
23443         Roo.each(this.formats, function(f) {
23444             style.menu.items.push({
23445                 xtype :'MenuItem',
23446                 xns: Roo.bootstrap,
23447                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23448                 tagname : f,
23449                 listeners : {
23450                     click : function()
23451                     {
23452                         editorcore.insertTag(this.tagname);
23453                         editor.focus();
23454                     }
23455                 }
23456                 
23457             });
23458         });
23459         children.push(style);   
23460         
23461         btn('bold',false,true);
23462         btn('italic',false,true);
23463         btn('align-left', 'justifyleft',true);
23464         btn('align-center', 'justifycenter',true);
23465         btn('align-right' , 'justifyright',true);
23466         btn('link', false, false, function(btn) {
23467             //Roo.log("create link?");
23468             var url = prompt(this.createLinkText, this.defaultLinkValue);
23469             if(url && url != 'http:/'+'/'){
23470                 this.editorcore.relayCmd('createlink', url);
23471             }
23472         }),
23473         btn('list','insertunorderedlist',true);
23474         btn('pencil', false,true, function(btn){
23475                 Roo.log(this);
23476                 this.toggleSourceEdit(btn.pressed);
23477         });
23478         
23479         if (this.editor.btns.length > 0) {
23480             for (var i = 0; i<this.editor.btns.length; i++) {
23481                 children.push(this.editor.btns[i]);
23482             }
23483         }
23484         
23485         /*
23486         var cog = {
23487                 xtype: 'Button',
23488                 size : 'sm',
23489                 xns: Roo.bootstrap,
23490                 glyphicon : 'cog',
23491                 //html : 'submit'
23492                 menu : {
23493                     xtype: 'Menu',
23494                     xns: Roo.bootstrap,
23495                     items:  []
23496                 }
23497         };
23498         
23499         cog.menu.items.push({
23500             xtype :'MenuItem',
23501             xns: Roo.bootstrap,
23502             html : Clean styles,
23503             tagname : f,
23504             listeners : {
23505                 click : function()
23506                 {
23507                     editorcore.insertTag(this.tagname);
23508                     editor.focus();
23509                 }
23510             }
23511             
23512         });
23513        */
23514         
23515          
23516        this.xtype = 'NavSimplebar';
23517         
23518         for(var i=0;i< children.length;i++) {
23519             
23520             this.buttons.add(this.addxtypeChild(children[i]));
23521             
23522         }
23523         
23524         editor.on('editorevent', this.updateToolbar, this);
23525     },
23526     onBtnClick : function(id)
23527     {
23528        this.editorcore.relayCmd(id);
23529        this.editorcore.focus();
23530     },
23531     
23532     /**
23533      * Protected method that will not generally be called directly. It triggers
23534      * a toolbar update by reading the markup state of the current selection in the editor.
23535      */
23536     updateToolbar: function(){
23537
23538         if(!this.editorcore.activated){
23539             this.editor.onFirstFocus(); // is this neeed?
23540             return;
23541         }
23542
23543         var btns = this.buttons; 
23544         var doc = this.editorcore.doc;
23545         btns.get('bold').setActive(doc.queryCommandState('bold'));
23546         btns.get('italic').setActive(doc.queryCommandState('italic'));
23547         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23548         
23549         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23550         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23551         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23552         
23553         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23554         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23555          /*
23556         
23557         var ans = this.editorcore.getAllAncestors();
23558         if (this.formatCombo) {
23559             
23560             
23561             var store = this.formatCombo.store;
23562             this.formatCombo.setValue("");
23563             for (var i =0; i < ans.length;i++) {
23564                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23565                     // select it..
23566                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23567                     break;
23568                 }
23569             }
23570         }
23571         
23572         
23573         
23574         // hides menus... - so this cant be on a menu...
23575         Roo.bootstrap.MenuMgr.hideAll();
23576         */
23577         Roo.bootstrap.MenuMgr.hideAll();
23578         //this.editorsyncValue();
23579     },
23580     onFirstFocus: function() {
23581         this.buttons.each(function(item){
23582            item.enable();
23583         });
23584     },
23585     toggleSourceEdit : function(sourceEditMode){
23586         
23587           
23588         if(sourceEditMode){
23589             Roo.log("disabling buttons");
23590            this.buttons.each( function(item){
23591                 if(item.cmd != 'pencil'){
23592                     item.disable();
23593                 }
23594             });
23595           
23596         }else{
23597             Roo.log("enabling buttons");
23598             if(this.editorcore.initialized){
23599                 this.buttons.each( function(item){
23600                     item.enable();
23601                 });
23602             }
23603             
23604         }
23605         Roo.log("calling toggole on editor");
23606         // tell the editor that it's been pressed..
23607         this.editor.toggleSourceEdit(sourceEditMode);
23608        
23609     }
23610 });
23611
23612
23613
23614
23615
23616 /**
23617  * @class Roo.bootstrap.Table.AbstractSelectionModel
23618  * @extends Roo.util.Observable
23619  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23620  * implemented by descendant classes.  This class should not be directly instantiated.
23621  * @constructor
23622  */
23623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23624     this.locked = false;
23625     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23626 };
23627
23628
23629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23630     /** @ignore Called by the grid automatically. Do not call directly. */
23631     init : function(grid){
23632         this.grid = grid;
23633         this.initEvents();
23634     },
23635
23636     /**
23637      * Locks the selections.
23638      */
23639     lock : function(){
23640         this.locked = true;
23641     },
23642
23643     /**
23644      * Unlocks the selections.
23645      */
23646     unlock : function(){
23647         this.locked = false;
23648     },
23649
23650     /**
23651      * Returns true if the selections are locked.
23652      * @return {Boolean}
23653      */
23654     isLocked : function(){
23655         return this.locked;
23656     }
23657 });
23658 /**
23659  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23660  * @class Roo.bootstrap.Table.RowSelectionModel
23661  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23662  * It supports multiple selections and keyboard selection/navigation. 
23663  * @constructor
23664  * @param {Object} config
23665  */
23666
23667 Roo.bootstrap.Table.RowSelectionModel = function(config){
23668     Roo.apply(this, config);
23669     this.selections = new Roo.util.MixedCollection(false, function(o){
23670         return o.id;
23671     });
23672
23673     this.last = false;
23674     this.lastActive = false;
23675
23676     this.addEvents({
23677         /**
23678              * @event selectionchange
23679              * Fires when the selection changes
23680              * @param {SelectionModel} this
23681              */
23682             "selectionchange" : true,
23683         /**
23684              * @event afterselectionchange
23685              * Fires after the selection changes (eg. by key press or clicking)
23686              * @param {SelectionModel} this
23687              */
23688             "afterselectionchange" : true,
23689         /**
23690              * @event beforerowselect
23691              * Fires when a row is selected being selected, return false to cancel.
23692              * @param {SelectionModel} this
23693              * @param {Number} rowIndex The selected index
23694              * @param {Boolean} keepExisting False if other selections will be cleared
23695              */
23696             "beforerowselect" : true,
23697         /**
23698              * @event rowselect
23699              * Fires when a row is selected.
23700              * @param {SelectionModel} this
23701              * @param {Number} rowIndex The selected index
23702              * @param {Roo.data.Record} r The record
23703              */
23704             "rowselect" : true,
23705         /**
23706              * @event rowdeselect
23707              * Fires when a row is deselected.
23708              * @param {SelectionModel} this
23709              * @param {Number} rowIndex The selected index
23710              */
23711         "rowdeselect" : true
23712     });
23713     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23714     this.locked = false;
23715  };
23716
23717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23718     /**
23719      * @cfg {Boolean} singleSelect
23720      * True to allow selection of only one row at a time (defaults to false)
23721      */
23722     singleSelect : false,
23723
23724     // private
23725     initEvents : function()
23726     {
23727
23728         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23729         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23730         //}else{ // allow click to work like normal
23731          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23732         //}
23733         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23734         this.grid.on("rowclick", this.handleMouseDown, this);
23735         
23736         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23737             "up" : function(e){
23738                 if(!e.shiftKey){
23739                     this.selectPrevious(e.shiftKey);
23740                 }else if(this.last !== false && this.lastActive !== false){
23741                     var last = this.last;
23742                     this.selectRange(this.last,  this.lastActive-1);
23743                     this.grid.getView().focusRow(this.lastActive);
23744                     if(last !== false){
23745                         this.last = last;
23746                     }
23747                 }else{
23748                     this.selectFirstRow();
23749                 }
23750                 this.fireEvent("afterselectionchange", this);
23751             },
23752             "down" : function(e){
23753                 if(!e.shiftKey){
23754                     this.selectNext(e.shiftKey);
23755                 }else if(this.last !== false && this.lastActive !== false){
23756                     var last = this.last;
23757                     this.selectRange(this.last,  this.lastActive+1);
23758                     this.grid.getView().focusRow(this.lastActive);
23759                     if(last !== false){
23760                         this.last = last;
23761                     }
23762                 }else{
23763                     this.selectFirstRow();
23764                 }
23765                 this.fireEvent("afterselectionchange", this);
23766             },
23767             scope: this
23768         });
23769         this.grid.store.on('load', function(){
23770             this.selections.clear();
23771         },this);
23772         /*
23773         var view = this.grid.view;
23774         view.on("refresh", this.onRefresh, this);
23775         view.on("rowupdated", this.onRowUpdated, this);
23776         view.on("rowremoved", this.onRemove, this);
23777         */
23778     },
23779
23780     // private
23781     onRefresh : function()
23782     {
23783         var ds = this.grid.store, i, v = this.grid.view;
23784         var s = this.selections;
23785         s.each(function(r){
23786             if((i = ds.indexOfId(r.id)) != -1){
23787                 v.onRowSelect(i);
23788             }else{
23789                 s.remove(r);
23790             }
23791         });
23792     },
23793
23794     // private
23795     onRemove : function(v, index, r){
23796         this.selections.remove(r);
23797     },
23798
23799     // private
23800     onRowUpdated : function(v, index, r){
23801         if(this.isSelected(r)){
23802             v.onRowSelect(index);
23803         }
23804     },
23805
23806     /**
23807      * Select records.
23808      * @param {Array} records The records to select
23809      * @param {Boolean} keepExisting (optional) True to keep existing selections
23810      */
23811     selectRecords : function(records, keepExisting)
23812     {
23813         if(!keepExisting){
23814             this.clearSelections();
23815         }
23816             var ds = this.grid.store;
23817         for(var i = 0, len = records.length; i < len; i++){
23818             this.selectRow(ds.indexOf(records[i]), true);
23819         }
23820     },
23821
23822     /**
23823      * Gets the number of selected rows.
23824      * @return {Number}
23825      */
23826     getCount : function(){
23827         return this.selections.length;
23828     },
23829
23830     /**
23831      * Selects the first row in the grid.
23832      */
23833     selectFirstRow : function(){
23834         this.selectRow(0);
23835     },
23836
23837     /**
23838      * Select the last row.
23839      * @param {Boolean} keepExisting (optional) True to keep existing selections
23840      */
23841     selectLastRow : function(keepExisting){
23842         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23843         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23844     },
23845
23846     /**
23847      * Selects the row immediately following the last selected row.
23848      * @param {Boolean} keepExisting (optional) True to keep existing selections
23849      */
23850     selectNext : function(keepExisting)
23851     {
23852             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23853             this.selectRow(this.last+1, keepExisting);
23854             this.grid.getView().focusRow(this.last);
23855         }
23856     },
23857
23858     /**
23859      * Selects the row that precedes the last selected row.
23860      * @param {Boolean} keepExisting (optional) True to keep existing selections
23861      */
23862     selectPrevious : function(keepExisting){
23863         if(this.last){
23864             this.selectRow(this.last-1, keepExisting);
23865             this.grid.getView().focusRow(this.last);
23866         }
23867     },
23868
23869     /**
23870      * Returns the selected records
23871      * @return {Array} Array of selected records
23872      */
23873     getSelections : function(){
23874         return [].concat(this.selections.items);
23875     },
23876
23877     /**
23878      * Returns the first selected record.
23879      * @return {Record}
23880      */
23881     getSelected : function(){
23882         return this.selections.itemAt(0);
23883     },
23884
23885
23886     /**
23887      * Clears all selections.
23888      */
23889     clearSelections : function(fast)
23890     {
23891         if(this.locked) {
23892             return;
23893         }
23894         if(fast !== true){
23895                 var ds = this.grid.store;
23896             var s = this.selections;
23897             s.each(function(r){
23898                 this.deselectRow(ds.indexOfId(r.id));
23899             }, this);
23900             s.clear();
23901         }else{
23902             this.selections.clear();
23903         }
23904         this.last = false;
23905     },
23906
23907
23908     /**
23909      * Selects all rows.
23910      */
23911     selectAll : function(){
23912         if(this.locked) {
23913             return;
23914         }
23915         this.selections.clear();
23916         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23917             this.selectRow(i, true);
23918         }
23919     },
23920
23921     /**
23922      * Returns True if there is a selection.
23923      * @return {Boolean}
23924      */
23925     hasSelection : function(){
23926         return this.selections.length > 0;
23927     },
23928
23929     /**
23930      * Returns True if the specified row is selected.
23931      * @param {Number/Record} record The record or index of the record to check
23932      * @return {Boolean}
23933      */
23934     isSelected : function(index){
23935             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23936         return (r && this.selections.key(r.id) ? true : false);
23937     },
23938
23939     /**
23940      * Returns True if the specified record id is selected.
23941      * @param {String} id The id of record to check
23942      * @return {Boolean}
23943      */
23944     isIdSelected : function(id){
23945         return (this.selections.key(id) ? true : false);
23946     },
23947
23948
23949     // private
23950     handleMouseDBClick : function(e, t){
23951         
23952     },
23953     // private
23954     handleMouseDown : function(e, t)
23955     {
23956             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23957         if(this.isLocked() || rowIndex < 0 ){
23958             return;
23959         };
23960         if(e.shiftKey && this.last !== false){
23961             var last = this.last;
23962             this.selectRange(last, rowIndex, e.ctrlKey);
23963             this.last = last; // reset the last
23964             t.focus();
23965     
23966         }else{
23967             var isSelected = this.isSelected(rowIndex);
23968             //Roo.log("select row:" + rowIndex);
23969             if(isSelected){
23970                 this.deselectRow(rowIndex);
23971             } else {
23972                         this.selectRow(rowIndex, true);
23973             }
23974     
23975             /*
23976                 if(e.button !== 0 && isSelected){
23977                 alert('rowIndex 2: ' + rowIndex);
23978                     view.focusRow(rowIndex);
23979                 }else if(e.ctrlKey && isSelected){
23980                     this.deselectRow(rowIndex);
23981                 }else if(!isSelected){
23982                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23983                     view.focusRow(rowIndex);
23984                 }
23985             */
23986         }
23987         this.fireEvent("afterselectionchange", this);
23988     },
23989     // private
23990     handleDragableRowClick :  function(grid, rowIndex, e) 
23991     {
23992         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23993             this.selectRow(rowIndex, false);
23994             grid.view.focusRow(rowIndex);
23995              this.fireEvent("afterselectionchange", this);
23996         }
23997     },
23998     
23999     /**
24000      * Selects multiple rows.
24001      * @param {Array} rows Array of the indexes of the row to select
24002      * @param {Boolean} keepExisting (optional) True to keep existing selections
24003      */
24004     selectRows : function(rows, keepExisting){
24005         if(!keepExisting){
24006             this.clearSelections();
24007         }
24008         for(var i = 0, len = rows.length; i < len; i++){
24009             this.selectRow(rows[i], true);
24010         }
24011     },
24012
24013     /**
24014      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24015      * @param {Number} startRow The index of the first row in the range
24016      * @param {Number} endRow The index of the last row in the range
24017      * @param {Boolean} keepExisting (optional) True to retain existing selections
24018      */
24019     selectRange : function(startRow, endRow, keepExisting){
24020         if(this.locked) {
24021             return;
24022         }
24023         if(!keepExisting){
24024             this.clearSelections();
24025         }
24026         if(startRow <= endRow){
24027             for(var i = startRow; i <= endRow; i++){
24028                 this.selectRow(i, true);
24029             }
24030         }else{
24031             for(var i = startRow; i >= endRow; i--){
24032                 this.selectRow(i, true);
24033             }
24034         }
24035     },
24036
24037     /**
24038      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24039      * @param {Number} startRow The index of the first row in the range
24040      * @param {Number} endRow The index of the last row in the range
24041      */
24042     deselectRange : function(startRow, endRow, preventViewNotify){
24043         if(this.locked) {
24044             return;
24045         }
24046         for(var i = startRow; i <= endRow; i++){
24047             this.deselectRow(i, preventViewNotify);
24048         }
24049     },
24050
24051     /**
24052      * Selects a row.
24053      * @param {Number} row The index of the row to select
24054      * @param {Boolean} keepExisting (optional) True to keep existing selections
24055      */
24056     selectRow : function(index, keepExisting, preventViewNotify)
24057     {
24058             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24059             return;
24060         }
24061         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24062             if(!keepExisting || this.singleSelect){
24063                 this.clearSelections();
24064             }
24065             
24066             var r = this.grid.store.getAt(index);
24067             //console.log('selectRow - record id :' + r.id);
24068             
24069             this.selections.add(r);
24070             this.last = this.lastActive = index;
24071             if(!preventViewNotify){
24072                 var proxy = new Roo.Element(
24073                                 this.grid.getRowDom(index)
24074                 );
24075                 proxy.addClass('bg-info info');
24076             }
24077             this.fireEvent("rowselect", this, index, r);
24078             this.fireEvent("selectionchange", this);
24079         }
24080     },
24081
24082     /**
24083      * Deselects a row.
24084      * @param {Number} row The index of the row to deselect
24085      */
24086     deselectRow : function(index, preventViewNotify)
24087     {
24088         if(this.locked) {
24089             return;
24090         }
24091         if(this.last == index){
24092             this.last = false;
24093         }
24094         if(this.lastActive == index){
24095             this.lastActive = false;
24096         }
24097         
24098         var r = this.grid.store.getAt(index);
24099         if (!r) {
24100             return;
24101         }
24102         
24103         this.selections.remove(r);
24104         //.console.log('deselectRow - record id :' + r.id);
24105         if(!preventViewNotify){
24106         
24107             var proxy = new Roo.Element(
24108                 this.grid.getRowDom(index)
24109             );
24110             proxy.removeClass('bg-info info');
24111         }
24112         this.fireEvent("rowdeselect", this, index);
24113         this.fireEvent("selectionchange", this);
24114     },
24115
24116     // private
24117     restoreLast : function(){
24118         if(this._last){
24119             this.last = this._last;
24120         }
24121     },
24122
24123     // private
24124     acceptsNav : function(row, col, cm){
24125         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24126     },
24127
24128     // private
24129     onEditorKey : function(field, e){
24130         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24131         if(k == e.TAB){
24132             e.stopEvent();
24133             ed.completeEdit();
24134             if(e.shiftKey){
24135                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24136             }else{
24137                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24138             }
24139         }else if(k == e.ENTER && !e.ctrlKey){
24140             e.stopEvent();
24141             ed.completeEdit();
24142             if(e.shiftKey){
24143                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24144             }else{
24145                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24146             }
24147         }else if(k == e.ESC){
24148             ed.cancelEdit();
24149         }
24150         if(newCell){
24151             g.startEditing(newCell[0], newCell[1]);
24152         }
24153     }
24154 });
24155 /*
24156  * Based on:
24157  * Ext JS Library 1.1.1
24158  * Copyright(c) 2006-2007, Ext JS, LLC.
24159  *
24160  * Originally Released Under LGPL - original licence link has changed is not relivant.
24161  *
24162  * Fork - LGPL
24163  * <script type="text/javascript">
24164  */
24165  
24166 /**
24167  * @class Roo.bootstrap.PagingToolbar
24168  * @extends Roo.bootstrap.NavSimplebar
24169  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24170  * @constructor
24171  * Create a new PagingToolbar
24172  * @param {Object} config The config object
24173  * @param {Roo.data.Store} store
24174  */
24175 Roo.bootstrap.PagingToolbar = function(config)
24176 {
24177     // old args format still supported... - xtype is prefered..
24178         // created from xtype...
24179     
24180     this.ds = config.dataSource;
24181     
24182     if (config.store && !this.ds) {
24183         this.store= Roo.factory(config.store, Roo.data);
24184         this.ds = this.store;
24185         this.ds.xmodule = this.xmodule || false;
24186     }
24187     
24188     this.toolbarItems = [];
24189     if (config.items) {
24190         this.toolbarItems = config.items;
24191     }
24192     
24193     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24194     
24195     this.cursor = 0;
24196     
24197     if (this.ds) { 
24198         this.bind(this.ds);
24199     }
24200     
24201     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24202     
24203 };
24204
24205 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24206     /**
24207      * @cfg {Roo.data.Store} dataSource
24208      * The underlying data store providing the paged data
24209      */
24210     /**
24211      * @cfg {String/HTMLElement/Element} container
24212      * container The id or element that will contain the toolbar
24213      */
24214     /**
24215      * @cfg {Boolean} displayInfo
24216      * True to display the displayMsg (defaults to false)
24217      */
24218     /**
24219      * @cfg {Number} pageSize
24220      * The number of records to display per page (defaults to 20)
24221      */
24222     pageSize: 20,
24223     /**
24224      * @cfg {String} displayMsg
24225      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24226      */
24227     displayMsg : 'Displaying {0} - {1} of {2}',
24228     /**
24229      * @cfg {String} emptyMsg
24230      * The message to display when no records are found (defaults to "No data to display")
24231      */
24232     emptyMsg : 'No data to display',
24233     /**
24234      * Customizable piece of the default paging text (defaults to "Page")
24235      * @type String
24236      */
24237     beforePageText : "Page",
24238     /**
24239      * Customizable piece of the default paging text (defaults to "of %0")
24240      * @type String
24241      */
24242     afterPageText : "of {0}",
24243     /**
24244      * Customizable piece of the default paging text (defaults to "First Page")
24245      * @type String
24246      */
24247     firstText : "First Page",
24248     /**
24249      * Customizable piece of the default paging text (defaults to "Previous Page")
24250      * @type String
24251      */
24252     prevText : "Previous Page",
24253     /**
24254      * Customizable piece of the default paging text (defaults to "Next Page")
24255      * @type String
24256      */
24257     nextText : "Next Page",
24258     /**
24259      * Customizable piece of the default paging text (defaults to "Last Page")
24260      * @type String
24261      */
24262     lastText : "Last Page",
24263     /**
24264      * Customizable piece of the default paging text (defaults to "Refresh")
24265      * @type String
24266      */
24267     refreshText : "Refresh",
24268
24269     buttons : false,
24270     // private
24271     onRender : function(ct, position) 
24272     {
24273         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24274         this.navgroup.parentId = this.id;
24275         this.navgroup.onRender(this.el, null);
24276         // add the buttons to the navgroup
24277         
24278         if(this.displayInfo){
24279             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24280             this.displayEl = this.el.select('.x-paging-info', true).first();
24281 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24282 //            this.displayEl = navel.el.select('span',true).first();
24283         }
24284         
24285         var _this = this;
24286         
24287         if(this.buttons){
24288             Roo.each(_this.buttons, function(e){ // this might need to use render????
24289                Roo.factory(e).onRender(_this.el, null);
24290             });
24291         }
24292             
24293         Roo.each(_this.toolbarItems, function(e) {
24294             _this.navgroup.addItem(e);
24295         });
24296         
24297         
24298         this.first = this.navgroup.addItem({
24299             tooltip: this.firstText,
24300             cls: "prev",
24301             icon : 'fa fa-backward',
24302             disabled: true,
24303             preventDefault: true,
24304             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24305         });
24306         
24307         this.prev =  this.navgroup.addItem({
24308             tooltip: this.prevText,
24309             cls: "prev",
24310             icon : 'fa fa-step-backward',
24311             disabled: true,
24312             preventDefault: true,
24313             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24314         });
24315     //this.addSeparator();
24316         
24317         
24318         var field = this.navgroup.addItem( {
24319             tagtype : 'span',
24320             cls : 'x-paging-position',
24321             
24322             html : this.beforePageText  +
24323                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24324                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24325          } ); //?? escaped?
24326         
24327         this.field = field.el.select('input', true).first();
24328         this.field.on("keydown", this.onPagingKeydown, this);
24329         this.field.on("focus", function(){this.dom.select();});
24330     
24331     
24332         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24333         //this.field.setHeight(18);
24334         //this.addSeparator();
24335         this.next = this.navgroup.addItem({
24336             tooltip: this.nextText,
24337             cls: "next",
24338             html : ' <i class="fa fa-step-forward">',
24339             disabled: true,
24340             preventDefault: true,
24341             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24342         });
24343         this.last = this.navgroup.addItem({
24344             tooltip: this.lastText,
24345             icon : 'fa fa-forward',
24346             cls: "next",
24347             disabled: true,
24348             preventDefault: true,
24349             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24350         });
24351     //this.addSeparator();
24352         this.loading = this.navgroup.addItem({
24353             tooltip: this.refreshText,
24354             icon: 'fa fa-refresh',
24355             preventDefault: true,
24356             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24357         });
24358         
24359     },
24360
24361     // private
24362     updateInfo : function(){
24363         if(this.displayEl){
24364             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24365             var msg = count == 0 ?
24366                 this.emptyMsg :
24367                 String.format(
24368                     this.displayMsg,
24369                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24370                 );
24371             this.displayEl.update(msg);
24372         }
24373     },
24374
24375     // private
24376     onLoad : function(ds, r, o)
24377     {
24378         this.cursor = o.params ? o.params.start : 0;
24379         var d = this.getPageData(),
24380             ap = d.activePage,
24381             ps = d.pages;
24382         
24383         
24384         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24385         this.field.dom.value = ap;
24386         this.first.setDisabled(ap == 1);
24387         this.prev.setDisabled(ap == 1);
24388         this.next.setDisabled(ap == ps);
24389         this.last.setDisabled(ap == ps);
24390         this.loading.enable();
24391         this.updateInfo();
24392     },
24393
24394     // private
24395     getPageData : function(){
24396         var total = this.ds.getTotalCount();
24397         return {
24398             total : total,
24399             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24400             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24401         };
24402     },
24403
24404     // private
24405     onLoadError : function(){
24406         this.loading.enable();
24407     },
24408
24409     // private
24410     onPagingKeydown : function(e){
24411         var k = e.getKey();
24412         var d = this.getPageData();
24413         if(k == e.RETURN){
24414             var v = this.field.dom.value, pageNum;
24415             if(!v || isNaN(pageNum = parseInt(v, 10))){
24416                 this.field.dom.value = d.activePage;
24417                 return;
24418             }
24419             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24420             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24421             e.stopEvent();
24422         }
24423         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))
24424         {
24425           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24426           this.field.dom.value = pageNum;
24427           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24428           e.stopEvent();
24429         }
24430         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24431         {
24432           var v = this.field.dom.value, pageNum; 
24433           var increment = (e.shiftKey) ? 10 : 1;
24434           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24435                 increment *= -1;
24436           }
24437           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24438             this.field.dom.value = d.activePage;
24439             return;
24440           }
24441           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24442           {
24443             this.field.dom.value = parseInt(v, 10) + increment;
24444             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24445             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24446           }
24447           e.stopEvent();
24448         }
24449     },
24450
24451     // private
24452     beforeLoad : function(){
24453         if(this.loading){
24454             this.loading.disable();
24455         }
24456     },
24457
24458     // private
24459     onClick : function(which){
24460         
24461         var ds = this.ds;
24462         if (!ds) {
24463             return;
24464         }
24465         
24466         switch(which){
24467             case "first":
24468                 ds.load({params:{start: 0, limit: this.pageSize}});
24469             break;
24470             case "prev":
24471                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24472             break;
24473             case "next":
24474                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24475             break;
24476             case "last":
24477                 var total = ds.getTotalCount();
24478                 var extra = total % this.pageSize;
24479                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24480                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24481             break;
24482             case "refresh":
24483                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24484             break;
24485         }
24486     },
24487
24488     /**
24489      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24490      * @param {Roo.data.Store} store The data store to unbind
24491      */
24492     unbind : function(ds){
24493         ds.un("beforeload", this.beforeLoad, this);
24494         ds.un("load", this.onLoad, this);
24495         ds.un("loadexception", this.onLoadError, this);
24496         ds.un("remove", this.updateInfo, this);
24497         ds.un("add", this.updateInfo, this);
24498         this.ds = undefined;
24499     },
24500
24501     /**
24502      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24503      * @param {Roo.data.Store} store The data store to bind
24504      */
24505     bind : function(ds){
24506         ds.on("beforeload", this.beforeLoad, this);
24507         ds.on("load", this.onLoad, this);
24508         ds.on("loadexception", this.onLoadError, this);
24509         ds.on("remove", this.updateInfo, this);
24510         ds.on("add", this.updateInfo, this);
24511         this.ds = ds;
24512     }
24513 });/*
24514  * - LGPL
24515  *
24516  * element
24517  * 
24518  */
24519
24520 /**
24521  * @class Roo.bootstrap.MessageBar
24522  * @extends Roo.bootstrap.Component
24523  * Bootstrap MessageBar class
24524  * @cfg {String} html contents of the MessageBar
24525  * @cfg {String} weight (info | success | warning | danger) default info
24526  * @cfg {String} beforeClass insert the bar before the given class
24527  * @cfg {Boolean} closable (true | false) default false
24528  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24529  * 
24530  * @constructor
24531  * Create a new Element
24532  * @param {Object} config The config object
24533  */
24534
24535 Roo.bootstrap.MessageBar = function(config){
24536     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24537 };
24538
24539 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24540     
24541     html: '',
24542     weight: 'info',
24543     closable: false,
24544     fixed: false,
24545     beforeClass: 'bootstrap-sticky-wrap',
24546     
24547     getAutoCreate : function(){
24548         
24549         var cfg = {
24550             tag: 'div',
24551             cls: 'alert alert-dismissable alert-' + this.weight,
24552             cn: [
24553                 {
24554                     tag: 'span',
24555                     cls: 'message',
24556                     html: this.html || ''
24557                 }
24558             ]
24559         };
24560         
24561         if(this.fixed){
24562             cfg.cls += ' alert-messages-fixed';
24563         }
24564         
24565         if(this.closable){
24566             cfg.cn.push({
24567                 tag: 'button',
24568                 cls: 'close',
24569                 html: 'x'
24570             });
24571         }
24572         
24573         return cfg;
24574     },
24575     
24576     onRender : function(ct, position)
24577     {
24578         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24579         
24580         if(!this.el){
24581             var cfg = Roo.apply({},  this.getAutoCreate());
24582             cfg.id = Roo.id();
24583             
24584             if (this.cls) {
24585                 cfg.cls += ' ' + this.cls;
24586             }
24587             if (this.style) {
24588                 cfg.style = this.style;
24589             }
24590             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24591             
24592             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24593         }
24594         
24595         this.el.select('>button.close').on('click', this.hide, this);
24596         
24597     },
24598     
24599     show : function()
24600     {
24601         if (!this.rendered) {
24602             this.render();
24603         }
24604         
24605         this.el.show();
24606         
24607         this.fireEvent('show', this);
24608         
24609     },
24610     
24611     hide : function()
24612     {
24613         if (!this.rendered) {
24614             this.render();
24615         }
24616         
24617         this.el.hide();
24618         
24619         this.fireEvent('hide', this);
24620     },
24621     
24622     update : function()
24623     {
24624 //        var e = this.el.dom.firstChild;
24625 //        
24626 //        if(this.closable){
24627 //            e = e.nextSibling;
24628 //        }
24629 //        
24630 //        e.data = this.html || '';
24631
24632         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24633     }
24634    
24635 });
24636
24637  
24638
24639      /*
24640  * - LGPL
24641  *
24642  * Graph
24643  * 
24644  */
24645
24646
24647 /**
24648  * @class Roo.bootstrap.Graph
24649  * @extends Roo.bootstrap.Component
24650  * Bootstrap Graph class
24651 > Prameters
24652  -sm {number} sm 4
24653  -md {number} md 5
24654  @cfg {String} graphtype  bar | vbar | pie
24655  @cfg {number} g_x coodinator | centre x (pie)
24656  @cfg {number} g_y coodinator | centre y (pie)
24657  @cfg {number} g_r radius (pie)
24658  @cfg {number} g_height height of the chart (respected by all elements in the set)
24659  @cfg {number} g_width width of the chart (respected by all elements in the set)
24660  @cfg {Object} title The title of the chart
24661     
24662  -{Array}  values
24663  -opts (object) options for the chart 
24664      o {
24665      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24666      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24667      o vgutter (number)
24668      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.
24669      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24670      o to
24671      o stretch (boolean)
24672      o }
24673  -opts (object) options for the pie
24674      o{
24675      o cut
24676      o startAngle (number)
24677      o endAngle (number)
24678      } 
24679  *
24680  * @constructor
24681  * Create a new Input
24682  * @param {Object} config The config object
24683  */
24684
24685 Roo.bootstrap.Graph = function(config){
24686     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24687     
24688     this.addEvents({
24689         // img events
24690         /**
24691          * @event click
24692          * The img click event for the img.
24693          * @param {Roo.EventObject} e
24694          */
24695         "click" : true
24696     });
24697 };
24698
24699 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24700     
24701     sm: 4,
24702     md: 5,
24703     graphtype: 'bar',
24704     g_height: 250,
24705     g_width: 400,
24706     g_x: 50,
24707     g_y: 50,
24708     g_r: 30,
24709     opts:{
24710         //g_colors: this.colors,
24711         g_type: 'soft',
24712         g_gutter: '20%'
24713
24714     },
24715     title : false,
24716
24717     getAutoCreate : function(){
24718         
24719         var cfg = {
24720             tag: 'div',
24721             html : null
24722         };
24723         
24724         
24725         return  cfg;
24726     },
24727
24728     onRender : function(ct,position){
24729         
24730         
24731         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24732         
24733         if (typeof(Raphael) == 'undefined') {
24734             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24735             return;
24736         }
24737         
24738         this.raphael = Raphael(this.el.dom);
24739         
24740                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24741                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24742                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24743                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24744                 /*
24745                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24746                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24747                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24748                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24749                 
24750                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24751                 r.barchart(330, 10, 300, 220, data1);
24752                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24753                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24754                 */
24755                 
24756                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24757                 // r.barchart(30, 30, 560, 250,  xdata, {
24758                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24759                 //     axis : "0 0 1 1",
24760                 //     axisxlabels :  xdata
24761                 //     //yvalues : cols,
24762                    
24763                 // });
24764 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24765 //        
24766 //        this.load(null,xdata,{
24767 //                axis : "0 0 1 1",
24768 //                axisxlabels :  xdata
24769 //                });
24770
24771     },
24772
24773     load : function(graphtype,xdata,opts)
24774     {
24775         this.raphael.clear();
24776         if(!graphtype) {
24777             graphtype = this.graphtype;
24778         }
24779         if(!opts){
24780             opts = this.opts;
24781         }
24782         var r = this.raphael,
24783             fin = function () {
24784                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24785             },
24786             fout = function () {
24787                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24788             },
24789             pfin = function() {
24790                 this.sector.stop();
24791                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24792
24793                 if (this.label) {
24794                     this.label[0].stop();
24795                     this.label[0].attr({ r: 7.5 });
24796                     this.label[1].attr({ "font-weight": 800 });
24797                 }
24798             },
24799             pfout = function() {
24800                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24801
24802                 if (this.label) {
24803                     this.label[0].animate({ r: 5 }, 500, "bounce");
24804                     this.label[1].attr({ "font-weight": 400 });
24805                 }
24806             };
24807
24808         switch(graphtype){
24809             case 'bar':
24810                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24811                 break;
24812             case 'hbar':
24813                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24814                 break;
24815             case 'pie':
24816 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24817 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24818 //            
24819                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24820                 
24821                 break;
24822
24823         }
24824         
24825         if(this.title){
24826             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24827         }
24828         
24829     },
24830     
24831     setTitle: function(o)
24832     {
24833         this.title = o;
24834     },
24835     
24836     initEvents: function() {
24837         
24838         if(!this.href){
24839             this.el.on('click', this.onClick, this);
24840         }
24841     },
24842     
24843     onClick : function(e)
24844     {
24845         Roo.log('img onclick');
24846         this.fireEvent('click', this, e);
24847     }
24848    
24849 });
24850
24851  
24852 /*
24853  * - LGPL
24854  *
24855  * numberBox
24856  * 
24857  */
24858 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24859
24860 /**
24861  * @class Roo.bootstrap.dash.NumberBox
24862  * @extends Roo.bootstrap.Component
24863  * Bootstrap NumberBox class
24864  * @cfg {String} headline Box headline
24865  * @cfg {String} content Box content
24866  * @cfg {String} icon Box icon
24867  * @cfg {String} footer Footer text
24868  * @cfg {String} fhref Footer href
24869  * 
24870  * @constructor
24871  * Create a new NumberBox
24872  * @param {Object} config The config object
24873  */
24874
24875
24876 Roo.bootstrap.dash.NumberBox = function(config){
24877     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24878     
24879 };
24880
24881 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24882     
24883     headline : '',
24884     content : '',
24885     icon : '',
24886     footer : '',
24887     fhref : '',
24888     ficon : '',
24889     
24890     getAutoCreate : function(){
24891         
24892         var cfg = {
24893             tag : 'div',
24894             cls : 'small-box ',
24895             cn : [
24896                 {
24897                     tag : 'div',
24898                     cls : 'inner',
24899                     cn :[
24900                         {
24901                             tag : 'h3',
24902                             cls : 'roo-headline',
24903                             html : this.headline
24904                         },
24905                         {
24906                             tag : 'p',
24907                             cls : 'roo-content',
24908                             html : this.content
24909                         }
24910                     ]
24911                 }
24912             ]
24913         };
24914         
24915         if(this.icon){
24916             cfg.cn.push({
24917                 tag : 'div',
24918                 cls : 'icon',
24919                 cn :[
24920                     {
24921                         tag : 'i',
24922                         cls : 'ion ' + this.icon
24923                     }
24924                 ]
24925             });
24926         }
24927         
24928         if(this.footer){
24929             var footer = {
24930                 tag : 'a',
24931                 cls : 'small-box-footer',
24932                 href : this.fhref || '#',
24933                 html : this.footer
24934             };
24935             
24936             cfg.cn.push(footer);
24937             
24938         }
24939         
24940         return  cfg;
24941     },
24942
24943     onRender : function(ct,position){
24944         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24945
24946
24947        
24948                 
24949     },
24950
24951     setHeadline: function (value)
24952     {
24953         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24954     },
24955     
24956     setFooter: function (value, href)
24957     {
24958         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24959         
24960         if(href){
24961             this.el.select('a.small-box-footer',true).first().attr('href', href);
24962         }
24963         
24964     },
24965
24966     setContent: function (value)
24967     {
24968         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24969     },
24970
24971     initEvents: function() 
24972     {   
24973         
24974     }
24975     
24976 });
24977
24978  
24979 /*
24980  * - LGPL
24981  *
24982  * TabBox
24983  * 
24984  */
24985 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24986
24987 /**
24988  * @class Roo.bootstrap.dash.TabBox
24989  * @extends Roo.bootstrap.Component
24990  * Bootstrap TabBox class
24991  * @cfg {String} title Title of the TabBox
24992  * @cfg {String} icon Icon of the TabBox
24993  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24994  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24995  * 
24996  * @constructor
24997  * Create a new TabBox
24998  * @param {Object} config The config object
24999  */
25000
25001
25002 Roo.bootstrap.dash.TabBox = function(config){
25003     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25004     this.addEvents({
25005         // raw events
25006         /**
25007          * @event addpane
25008          * When a pane is added
25009          * @param {Roo.bootstrap.dash.TabPane} pane
25010          */
25011         "addpane" : true,
25012         /**
25013          * @event activatepane
25014          * When a pane is activated
25015          * @param {Roo.bootstrap.dash.TabPane} pane
25016          */
25017         "activatepane" : true
25018         
25019          
25020     });
25021     
25022     this.panes = [];
25023 };
25024
25025 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25026
25027     title : '',
25028     icon : false,
25029     showtabs : true,
25030     tabScrollable : false,
25031     
25032     getChildContainer : function()
25033     {
25034         return this.el.select('.tab-content', true).first();
25035     },
25036     
25037     getAutoCreate : function(){
25038         
25039         var header = {
25040             tag: 'li',
25041             cls: 'pull-left header',
25042             html: this.title,
25043             cn : []
25044         };
25045         
25046         if(this.icon){
25047             header.cn.push({
25048                 tag: 'i',
25049                 cls: 'fa ' + this.icon
25050             });
25051         }
25052         
25053         var h = {
25054             tag: 'ul',
25055             cls: 'nav nav-tabs pull-right',
25056             cn: [
25057                 header
25058             ]
25059         };
25060         
25061         if(this.tabScrollable){
25062             h = {
25063                 tag: 'div',
25064                 cls: 'tab-header',
25065                 cn: [
25066                     {
25067                         tag: 'ul',
25068                         cls: 'nav nav-tabs pull-right',
25069                         cn: [
25070                             header
25071                         ]
25072                     }
25073                 ]
25074             };
25075         }
25076         
25077         var cfg = {
25078             tag: 'div',
25079             cls: 'nav-tabs-custom',
25080             cn: [
25081                 h,
25082                 {
25083                     tag: 'div',
25084                     cls: 'tab-content no-padding',
25085                     cn: []
25086                 }
25087             ]
25088         };
25089
25090         return  cfg;
25091     },
25092     initEvents : function()
25093     {
25094         //Roo.log('add add pane handler');
25095         this.on('addpane', this.onAddPane, this);
25096     },
25097      /**
25098      * Updates the box title
25099      * @param {String} html to set the title to.
25100      */
25101     setTitle : function(value)
25102     {
25103         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25104     },
25105     onAddPane : function(pane)
25106     {
25107         this.panes.push(pane);
25108         //Roo.log('addpane');
25109         //Roo.log(pane);
25110         // tabs are rendere left to right..
25111         if(!this.showtabs){
25112             return;
25113         }
25114         
25115         var ctr = this.el.select('.nav-tabs', true).first();
25116          
25117          
25118         var existing = ctr.select('.nav-tab',true);
25119         var qty = existing.getCount();;
25120         
25121         
25122         var tab = ctr.createChild({
25123             tag : 'li',
25124             cls : 'nav-tab' + (qty ? '' : ' active'),
25125             cn : [
25126                 {
25127                     tag : 'a',
25128                     href:'#',
25129                     html : pane.title
25130                 }
25131             ]
25132         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25133         pane.tab = tab;
25134         
25135         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25136         if (!qty) {
25137             pane.el.addClass('active');
25138         }
25139         
25140                 
25141     },
25142     onTabClick : function(ev,un,ob,pane)
25143     {
25144         //Roo.log('tab - prev default');
25145         ev.preventDefault();
25146         
25147         
25148         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25149         pane.tab.addClass('active');
25150         //Roo.log(pane.title);
25151         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25152         // technically we should have a deactivate event.. but maybe add later.
25153         // and it should not de-activate the selected tab...
25154         this.fireEvent('activatepane', pane);
25155         pane.el.addClass('active');
25156         pane.fireEvent('activate');
25157         
25158         
25159     },
25160     
25161     getActivePane : function()
25162     {
25163         var r = false;
25164         Roo.each(this.panes, function(p) {
25165             if(p.el.hasClass('active')){
25166                 r = p;
25167                 return false;
25168             }
25169             
25170             return;
25171         });
25172         
25173         return r;
25174     }
25175     
25176     
25177 });
25178
25179  
25180 /*
25181  * - LGPL
25182  *
25183  * Tab pane
25184  * 
25185  */
25186 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25187 /**
25188  * @class Roo.bootstrap.TabPane
25189  * @extends Roo.bootstrap.Component
25190  * Bootstrap TabPane class
25191  * @cfg {Boolean} active (false | true) Default false
25192  * @cfg {String} title title of panel
25193
25194  * 
25195  * @constructor
25196  * Create a new TabPane
25197  * @param {Object} config The config object
25198  */
25199
25200 Roo.bootstrap.dash.TabPane = function(config){
25201     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25202     
25203     this.addEvents({
25204         // raw events
25205         /**
25206          * @event activate
25207          * When a pane is activated
25208          * @param {Roo.bootstrap.dash.TabPane} pane
25209          */
25210         "activate" : true
25211          
25212     });
25213 };
25214
25215 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25216     
25217     active : false,
25218     title : '',
25219     
25220     // the tabBox that this is attached to.
25221     tab : false,
25222      
25223     getAutoCreate : function() 
25224     {
25225         var cfg = {
25226             tag: 'div',
25227             cls: 'tab-pane'
25228         };
25229         
25230         if(this.active){
25231             cfg.cls += ' active';
25232         }
25233         
25234         return cfg;
25235     },
25236     initEvents  : function()
25237     {
25238         //Roo.log('trigger add pane handler');
25239         this.parent().fireEvent('addpane', this)
25240     },
25241     
25242      /**
25243      * Updates the tab title 
25244      * @param {String} html to set the title to.
25245      */
25246     setTitle: function(str)
25247     {
25248         if (!this.tab) {
25249             return;
25250         }
25251         this.title = str;
25252         this.tab.select('a', true).first().dom.innerHTML = str;
25253         
25254     }
25255     
25256     
25257     
25258 });
25259
25260  
25261
25262
25263  /*
25264  * - LGPL
25265  *
25266  * menu
25267  * 
25268  */
25269 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25270
25271 /**
25272  * @class Roo.bootstrap.menu.Menu
25273  * @extends Roo.bootstrap.Component
25274  * Bootstrap Menu class - container for Menu
25275  * @cfg {String} html Text of the menu
25276  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25277  * @cfg {String} icon Font awesome icon
25278  * @cfg {String} pos Menu align to (top | bottom) default bottom
25279  * 
25280  * 
25281  * @constructor
25282  * Create a new Menu
25283  * @param {Object} config The config object
25284  */
25285
25286
25287 Roo.bootstrap.menu.Menu = function(config){
25288     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25289     
25290     this.addEvents({
25291         /**
25292          * @event beforeshow
25293          * Fires before this menu is displayed
25294          * @param {Roo.bootstrap.menu.Menu} this
25295          */
25296         beforeshow : true,
25297         /**
25298          * @event beforehide
25299          * Fires before this menu is hidden
25300          * @param {Roo.bootstrap.menu.Menu} this
25301          */
25302         beforehide : true,
25303         /**
25304          * @event show
25305          * Fires after this menu is displayed
25306          * @param {Roo.bootstrap.menu.Menu} this
25307          */
25308         show : true,
25309         /**
25310          * @event hide
25311          * Fires after this menu is hidden
25312          * @param {Roo.bootstrap.menu.Menu} this
25313          */
25314         hide : true,
25315         /**
25316          * @event click
25317          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25318          * @param {Roo.bootstrap.menu.Menu} this
25319          * @param {Roo.EventObject} e
25320          */
25321         click : true
25322     });
25323     
25324 };
25325
25326 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25327     
25328     submenu : false,
25329     html : '',
25330     weight : 'default',
25331     icon : false,
25332     pos : 'bottom',
25333     
25334     
25335     getChildContainer : function() {
25336         if(this.isSubMenu){
25337             return this.el;
25338         }
25339         
25340         return this.el.select('ul.dropdown-menu', true).first();  
25341     },
25342     
25343     getAutoCreate : function()
25344     {
25345         var text = [
25346             {
25347                 tag : 'span',
25348                 cls : 'roo-menu-text',
25349                 html : this.html
25350             }
25351         ];
25352         
25353         if(this.icon){
25354             text.unshift({
25355                 tag : 'i',
25356                 cls : 'fa ' + this.icon
25357             })
25358         }
25359         
25360         
25361         var cfg = {
25362             tag : 'div',
25363             cls : 'btn-group',
25364             cn : [
25365                 {
25366                     tag : 'button',
25367                     cls : 'dropdown-button btn btn-' + this.weight,
25368                     cn : text
25369                 },
25370                 {
25371                     tag : 'button',
25372                     cls : 'dropdown-toggle btn btn-' + this.weight,
25373                     cn : [
25374                         {
25375                             tag : 'span',
25376                             cls : 'caret'
25377                         }
25378                     ]
25379                 },
25380                 {
25381                     tag : 'ul',
25382                     cls : 'dropdown-menu'
25383                 }
25384             ]
25385             
25386         };
25387         
25388         if(this.pos == 'top'){
25389             cfg.cls += ' dropup';
25390         }
25391         
25392         if(this.isSubMenu){
25393             cfg = {
25394                 tag : 'ul',
25395                 cls : 'dropdown-menu'
25396             }
25397         }
25398         
25399         return cfg;
25400     },
25401     
25402     onRender : function(ct, position)
25403     {
25404         this.isSubMenu = ct.hasClass('dropdown-submenu');
25405         
25406         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25407     },
25408     
25409     initEvents : function() 
25410     {
25411         if(this.isSubMenu){
25412             return;
25413         }
25414         
25415         this.hidden = true;
25416         
25417         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25418         this.triggerEl.on('click', this.onTriggerPress, this);
25419         
25420         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25421         this.buttonEl.on('click', this.onClick, this);
25422         
25423     },
25424     
25425     list : function()
25426     {
25427         if(this.isSubMenu){
25428             return this.el;
25429         }
25430         
25431         return this.el.select('ul.dropdown-menu', true).first();
25432     },
25433     
25434     onClick : function(e)
25435     {
25436         this.fireEvent("click", this, e);
25437     },
25438     
25439     onTriggerPress  : function(e)
25440     {   
25441         if (this.isVisible()) {
25442             this.hide();
25443         } else {
25444             this.show();
25445         }
25446     },
25447     
25448     isVisible : function(){
25449         return !this.hidden;
25450     },
25451     
25452     show : function()
25453     {
25454         this.fireEvent("beforeshow", this);
25455         
25456         this.hidden = false;
25457         this.el.addClass('open');
25458         
25459         Roo.get(document).on("mouseup", this.onMouseUp, this);
25460         
25461         this.fireEvent("show", this);
25462         
25463         
25464     },
25465     
25466     hide : function()
25467     {
25468         this.fireEvent("beforehide", this);
25469         
25470         this.hidden = true;
25471         this.el.removeClass('open');
25472         
25473         Roo.get(document).un("mouseup", this.onMouseUp);
25474         
25475         this.fireEvent("hide", this);
25476     },
25477     
25478     onMouseUp : function()
25479     {
25480         this.hide();
25481     }
25482     
25483 });
25484
25485  
25486  /*
25487  * - LGPL
25488  *
25489  * menu item
25490  * 
25491  */
25492 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25493
25494 /**
25495  * @class Roo.bootstrap.menu.Item
25496  * @extends Roo.bootstrap.Component
25497  * Bootstrap MenuItem class
25498  * @cfg {Boolean} submenu (true | false) default false
25499  * @cfg {String} html text of the item
25500  * @cfg {String} href the link
25501  * @cfg {Boolean} disable (true | false) default false
25502  * @cfg {Boolean} preventDefault (true | false) default true
25503  * @cfg {String} icon Font awesome icon
25504  * @cfg {String} pos Submenu align to (left | right) default right 
25505  * 
25506  * 
25507  * @constructor
25508  * Create a new Item
25509  * @param {Object} config The config object
25510  */
25511
25512
25513 Roo.bootstrap.menu.Item = function(config){
25514     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25515     this.addEvents({
25516         /**
25517          * @event mouseover
25518          * Fires when the mouse is hovering over this menu
25519          * @param {Roo.bootstrap.menu.Item} this
25520          * @param {Roo.EventObject} e
25521          */
25522         mouseover : true,
25523         /**
25524          * @event mouseout
25525          * Fires when the mouse exits this menu
25526          * @param {Roo.bootstrap.menu.Item} this
25527          * @param {Roo.EventObject} e
25528          */
25529         mouseout : true,
25530         // raw events
25531         /**
25532          * @event click
25533          * The raw click event for the entire grid.
25534          * @param {Roo.EventObject} e
25535          */
25536         click : true
25537     });
25538 };
25539
25540 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25541     
25542     submenu : false,
25543     href : '',
25544     html : '',
25545     preventDefault: true,
25546     disable : false,
25547     icon : false,
25548     pos : 'right',
25549     
25550     getAutoCreate : function()
25551     {
25552         var text = [
25553             {
25554                 tag : 'span',
25555                 cls : 'roo-menu-item-text',
25556                 html : this.html
25557             }
25558         ];
25559         
25560         if(this.icon){
25561             text.unshift({
25562                 tag : 'i',
25563                 cls : 'fa ' + this.icon
25564             })
25565         }
25566         
25567         var cfg = {
25568             tag : 'li',
25569             cn : [
25570                 {
25571                     tag : 'a',
25572                     href : this.href || '#',
25573                     cn : text
25574                 }
25575             ]
25576         };
25577         
25578         if(this.disable){
25579             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25580         }
25581         
25582         if(this.submenu){
25583             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25584             
25585             if(this.pos == 'left'){
25586                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25587             }
25588         }
25589         
25590         return cfg;
25591     },
25592     
25593     initEvents : function() 
25594     {
25595         this.el.on('mouseover', this.onMouseOver, this);
25596         this.el.on('mouseout', this.onMouseOut, this);
25597         
25598         this.el.select('a', true).first().on('click', this.onClick, this);
25599         
25600     },
25601     
25602     onClick : function(e)
25603     {
25604         if(this.preventDefault){
25605             e.preventDefault();
25606         }
25607         
25608         this.fireEvent("click", this, e);
25609     },
25610     
25611     onMouseOver : function(e)
25612     {
25613         if(this.submenu && this.pos == 'left'){
25614             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25615         }
25616         
25617         this.fireEvent("mouseover", this, e);
25618     },
25619     
25620     onMouseOut : function(e)
25621     {
25622         this.fireEvent("mouseout", this, e);
25623     }
25624 });
25625
25626  
25627
25628  /*
25629  * - LGPL
25630  *
25631  * menu separator
25632  * 
25633  */
25634 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25635
25636 /**
25637  * @class Roo.bootstrap.menu.Separator
25638  * @extends Roo.bootstrap.Component
25639  * Bootstrap Separator class
25640  * 
25641  * @constructor
25642  * Create a new Separator
25643  * @param {Object} config The config object
25644  */
25645
25646
25647 Roo.bootstrap.menu.Separator = function(config){
25648     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25649 };
25650
25651 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25652     
25653     getAutoCreate : function(){
25654         var cfg = {
25655             tag : 'li',
25656             cls: 'divider'
25657         };
25658         
25659         return cfg;
25660     }
25661    
25662 });
25663
25664  
25665
25666  /*
25667  * - LGPL
25668  *
25669  * Tooltip
25670  * 
25671  */
25672
25673 /**
25674  * @class Roo.bootstrap.Tooltip
25675  * Bootstrap Tooltip class
25676  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25677  * to determine which dom element triggers the tooltip.
25678  * 
25679  * It needs to add support for additional attributes like tooltip-position
25680  * 
25681  * @constructor
25682  * Create a new Toolti
25683  * @param {Object} config The config object
25684  */
25685
25686 Roo.bootstrap.Tooltip = function(config){
25687     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25688     
25689     this.alignment = Roo.bootstrap.Tooltip.alignment;
25690     
25691     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25692         this.alignment = config.alignment;
25693     }
25694     
25695 };
25696
25697 Roo.apply(Roo.bootstrap.Tooltip, {
25698     /**
25699      * @function init initialize tooltip monitoring.
25700      * @static
25701      */
25702     currentEl : false,
25703     currentTip : false,
25704     currentRegion : false,
25705     
25706     //  init : delay?
25707     
25708     init : function()
25709     {
25710         Roo.get(document).on('mouseover', this.enter ,this);
25711         Roo.get(document).on('mouseout', this.leave, this);
25712          
25713         
25714         this.currentTip = new Roo.bootstrap.Tooltip();
25715     },
25716     
25717     enter : function(ev)
25718     {
25719         var dom = ev.getTarget();
25720         
25721         //Roo.log(['enter',dom]);
25722         var el = Roo.fly(dom);
25723         if (this.currentEl) {
25724             //Roo.log(dom);
25725             //Roo.log(this.currentEl);
25726             //Roo.log(this.currentEl.contains(dom));
25727             if (this.currentEl == el) {
25728                 return;
25729             }
25730             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25731                 return;
25732             }
25733
25734         }
25735         
25736         if (this.currentTip.el) {
25737             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25738         }    
25739         //Roo.log(ev);
25740         
25741         if(!el || el.dom == document){
25742             return;
25743         }
25744         
25745         var bindEl = el;
25746         
25747         // you can not look for children, as if el is the body.. then everythign is the child..
25748         if (!el.attr('tooltip')) { //
25749             if (!el.select("[tooltip]").elements.length) {
25750                 return;
25751             }
25752             // is the mouse over this child...?
25753             bindEl = el.select("[tooltip]").first();
25754             var xy = ev.getXY();
25755             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25756                 //Roo.log("not in region.");
25757                 return;
25758             }
25759             //Roo.log("child element over..");
25760             
25761         }
25762         this.currentEl = bindEl;
25763         this.currentTip.bind(bindEl);
25764         this.currentRegion = Roo.lib.Region.getRegion(dom);
25765         this.currentTip.enter();
25766         
25767     },
25768     leave : function(ev)
25769     {
25770         var dom = ev.getTarget();
25771         //Roo.log(['leave',dom]);
25772         if (!this.currentEl) {
25773             return;
25774         }
25775         
25776         
25777         if (dom != this.currentEl.dom) {
25778             return;
25779         }
25780         var xy = ev.getXY();
25781         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25782             return;
25783         }
25784         // only activate leave if mouse cursor is outside... bounding box..
25785         
25786         
25787         
25788         
25789         if (this.currentTip) {
25790             this.currentTip.leave();
25791         }
25792         //Roo.log('clear currentEl');
25793         this.currentEl = false;
25794         
25795         
25796     },
25797     alignment : {
25798         'left' : ['r-l', [-2,0], 'right'],
25799         'right' : ['l-r', [2,0], 'left'],
25800         'bottom' : ['t-b', [0,2], 'top'],
25801         'top' : [ 'b-t', [0,-2], 'bottom']
25802     }
25803     
25804 });
25805
25806
25807 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25808     
25809     
25810     bindEl : false,
25811     
25812     delay : null, // can be { show : 300 , hide: 500}
25813     
25814     timeout : null,
25815     
25816     hoverState : null, //???
25817     
25818     placement : 'bottom', 
25819     
25820     alignment : false,
25821     
25822     getAutoCreate : function(){
25823     
25824         var cfg = {
25825            cls : 'tooltip',
25826            role : 'tooltip',
25827            cn : [
25828                 {
25829                     cls : 'tooltip-arrow'
25830                 },
25831                 {
25832                     cls : 'tooltip-inner'
25833                 }
25834            ]
25835         };
25836         
25837         return cfg;
25838     },
25839     bind : function(el)
25840     {
25841         this.bindEl = el;
25842     },
25843       
25844     
25845     enter : function () {
25846        
25847         if (this.timeout != null) {
25848             clearTimeout(this.timeout);
25849         }
25850         
25851         this.hoverState = 'in';
25852          //Roo.log("enter - show");
25853         if (!this.delay || !this.delay.show) {
25854             this.show();
25855             return;
25856         }
25857         var _t = this;
25858         this.timeout = setTimeout(function () {
25859             if (_t.hoverState == 'in') {
25860                 _t.show();
25861             }
25862         }, this.delay.show);
25863     },
25864     leave : function()
25865     {
25866         clearTimeout(this.timeout);
25867     
25868         this.hoverState = 'out';
25869          if (!this.delay || !this.delay.hide) {
25870             this.hide();
25871             return;
25872         }
25873        
25874         var _t = this;
25875         this.timeout = setTimeout(function () {
25876             //Roo.log("leave - timeout");
25877             
25878             if (_t.hoverState == 'out') {
25879                 _t.hide();
25880                 Roo.bootstrap.Tooltip.currentEl = false;
25881             }
25882         }, delay);
25883     },
25884     
25885     show : function (msg)
25886     {
25887         if (!this.el) {
25888             this.render(document.body);
25889         }
25890         // set content.
25891         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25892         
25893         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25894         
25895         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25896         
25897         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25898         
25899         var placement = typeof this.placement == 'function' ?
25900             this.placement.call(this, this.el, on_el) :
25901             this.placement;
25902             
25903         var autoToken = /\s?auto?\s?/i;
25904         var autoPlace = autoToken.test(placement);
25905         if (autoPlace) {
25906             placement = placement.replace(autoToken, '') || 'top';
25907         }
25908         
25909         //this.el.detach()
25910         //this.el.setXY([0,0]);
25911         this.el.show();
25912         //this.el.dom.style.display='block';
25913         
25914         //this.el.appendTo(on_el);
25915         
25916         var p = this.getPosition();
25917         var box = this.el.getBox();
25918         
25919         if (autoPlace) {
25920             // fixme..
25921         }
25922         
25923         var align = this.alignment[placement];
25924         
25925         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25926         
25927         if(placement == 'top' || placement == 'bottom'){
25928             if(xy[0] < 0){
25929                 placement = 'right';
25930             }
25931             
25932             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25933                 placement = 'left';
25934             }
25935             
25936             var scroll = Roo.select('body', true).first().getScroll();
25937             
25938             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25939                 placement = 'top';
25940             }
25941             
25942         }
25943         
25944         this.el.alignTo(this.bindEl, align[0],align[1]);
25945         //var arrow = this.el.select('.arrow',true).first();
25946         //arrow.set(align[2], 
25947         
25948         this.el.addClass(placement);
25949         
25950         this.el.addClass('in fade');
25951         
25952         this.hoverState = null;
25953         
25954         if (this.el.hasClass('fade')) {
25955             // fade it?
25956         }
25957         
25958     },
25959     hide : function()
25960     {
25961          
25962         if (!this.el) {
25963             return;
25964         }
25965         //this.el.setXY([0,0]);
25966         this.el.removeClass('in');
25967         //this.el.hide();
25968         
25969     }
25970     
25971 });
25972  
25973
25974  /*
25975  * - LGPL
25976  *
25977  * Location Picker
25978  * 
25979  */
25980
25981 /**
25982  * @class Roo.bootstrap.LocationPicker
25983  * @extends Roo.bootstrap.Component
25984  * Bootstrap LocationPicker class
25985  * @cfg {Number} latitude Position when init default 0
25986  * @cfg {Number} longitude Position when init default 0
25987  * @cfg {Number} zoom default 15
25988  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25989  * @cfg {Boolean} mapTypeControl default false
25990  * @cfg {Boolean} disableDoubleClickZoom default false
25991  * @cfg {Boolean} scrollwheel default true
25992  * @cfg {Boolean} streetViewControl default false
25993  * @cfg {Number} radius default 0
25994  * @cfg {String} locationName
25995  * @cfg {Boolean} draggable default true
25996  * @cfg {Boolean} enableAutocomplete default false
25997  * @cfg {Boolean} enableReverseGeocode default true
25998  * @cfg {String} markerTitle
25999  * 
26000  * @constructor
26001  * Create a new LocationPicker
26002  * @param {Object} config The config object
26003  */
26004
26005
26006 Roo.bootstrap.LocationPicker = function(config){
26007     
26008     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26009     
26010     this.addEvents({
26011         /**
26012          * @event initial
26013          * Fires when the picker initialized.
26014          * @param {Roo.bootstrap.LocationPicker} this
26015          * @param {Google Location} location
26016          */
26017         initial : true,
26018         /**
26019          * @event positionchanged
26020          * Fires when the picker position changed.
26021          * @param {Roo.bootstrap.LocationPicker} this
26022          * @param {Google Location} location
26023          */
26024         positionchanged : true,
26025         /**
26026          * @event resize
26027          * Fires when the map resize.
26028          * @param {Roo.bootstrap.LocationPicker} this
26029          */
26030         resize : true,
26031         /**
26032          * @event show
26033          * Fires when the map show.
26034          * @param {Roo.bootstrap.LocationPicker} this
26035          */
26036         show : true,
26037         /**
26038          * @event hide
26039          * Fires when the map hide.
26040          * @param {Roo.bootstrap.LocationPicker} this
26041          */
26042         hide : true,
26043         /**
26044          * @event mapClick
26045          * Fires when click the map.
26046          * @param {Roo.bootstrap.LocationPicker} this
26047          * @param {Map event} e
26048          */
26049         mapClick : true,
26050         /**
26051          * @event mapRightClick
26052          * Fires when right click the map.
26053          * @param {Roo.bootstrap.LocationPicker} this
26054          * @param {Map event} e
26055          */
26056         mapRightClick : true,
26057         /**
26058          * @event markerClick
26059          * Fires when click the marker.
26060          * @param {Roo.bootstrap.LocationPicker} this
26061          * @param {Map event} e
26062          */
26063         markerClick : true,
26064         /**
26065          * @event markerRightClick
26066          * Fires when right click the marker.
26067          * @param {Roo.bootstrap.LocationPicker} this
26068          * @param {Map event} e
26069          */
26070         markerRightClick : true,
26071         /**
26072          * @event OverlayViewDraw
26073          * Fires when OverlayView Draw
26074          * @param {Roo.bootstrap.LocationPicker} this
26075          */
26076         OverlayViewDraw : true,
26077         /**
26078          * @event OverlayViewOnAdd
26079          * Fires when OverlayView Draw
26080          * @param {Roo.bootstrap.LocationPicker} this
26081          */
26082         OverlayViewOnAdd : true,
26083         /**
26084          * @event OverlayViewOnRemove
26085          * Fires when OverlayView Draw
26086          * @param {Roo.bootstrap.LocationPicker} this
26087          */
26088         OverlayViewOnRemove : true,
26089         /**
26090          * @event OverlayViewShow
26091          * Fires when OverlayView Draw
26092          * @param {Roo.bootstrap.LocationPicker} this
26093          * @param {Pixel} cpx
26094          */
26095         OverlayViewShow : true,
26096         /**
26097          * @event OverlayViewHide
26098          * Fires when OverlayView Draw
26099          * @param {Roo.bootstrap.LocationPicker} this
26100          */
26101         OverlayViewHide : true,
26102         /**
26103          * @event loadexception
26104          * Fires when load google lib failed.
26105          * @param {Roo.bootstrap.LocationPicker} this
26106          */
26107         loadexception : true
26108     });
26109         
26110 };
26111
26112 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26113     
26114     gMapContext: false,
26115     
26116     latitude: 0,
26117     longitude: 0,
26118     zoom: 15,
26119     mapTypeId: false,
26120     mapTypeControl: false,
26121     disableDoubleClickZoom: false,
26122     scrollwheel: true,
26123     streetViewControl: false,
26124     radius: 0,
26125     locationName: '',
26126     draggable: true,
26127     enableAutocomplete: false,
26128     enableReverseGeocode: true,
26129     markerTitle: '',
26130     
26131     getAutoCreate: function()
26132     {
26133
26134         var cfg = {
26135             tag: 'div',
26136             cls: 'roo-location-picker'
26137         };
26138         
26139         return cfg
26140     },
26141     
26142     initEvents: function(ct, position)
26143     {       
26144         if(!this.el.getWidth() || this.isApplied()){
26145             return;
26146         }
26147         
26148         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26149         
26150         this.initial();
26151     },
26152     
26153     initial: function()
26154     {
26155         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26156             this.fireEvent('loadexception', this);
26157             return;
26158         }
26159         
26160         if(!this.mapTypeId){
26161             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26162         }
26163         
26164         this.gMapContext = this.GMapContext();
26165         
26166         this.initOverlayView();
26167         
26168         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26169         
26170         var _this = this;
26171                 
26172         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26173             _this.setPosition(_this.gMapContext.marker.position);
26174         });
26175         
26176         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26177             _this.fireEvent('mapClick', this, event);
26178             
26179         });
26180
26181         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26182             _this.fireEvent('mapRightClick', this, event);
26183             
26184         });
26185         
26186         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26187             _this.fireEvent('markerClick', this, event);
26188             
26189         });
26190
26191         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26192             _this.fireEvent('markerRightClick', this, event);
26193             
26194         });
26195         
26196         this.setPosition(this.gMapContext.location);
26197         
26198         this.fireEvent('initial', this, this.gMapContext.location);
26199     },
26200     
26201     initOverlayView: function()
26202     {
26203         var _this = this;
26204         
26205         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26206             
26207             draw: function()
26208             {
26209                 _this.fireEvent('OverlayViewDraw', _this);
26210             },
26211             
26212             onAdd: function()
26213             {
26214                 _this.fireEvent('OverlayViewOnAdd', _this);
26215             },
26216             
26217             onRemove: function()
26218             {
26219                 _this.fireEvent('OverlayViewOnRemove', _this);
26220             },
26221             
26222             show: function(cpx)
26223             {
26224                 _this.fireEvent('OverlayViewShow', _this, cpx);
26225             },
26226             
26227             hide: function()
26228             {
26229                 _this.fireEvent('OverlayViewHide', _this);
26230             }
26231             
26232         });
26233     },
26234     
26235     fromLatLngToContainerPixel: function(event)
26236     {
26237         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26238     },
26239     
26240     isApplied: function() 
26241     {
26242         return this.getGmapContext() == false ? false : true;
26243     },
26244     
26245     getGmapContext: function() 
26246     {
26247         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26248     },
26249     
26250     GMapContext: function() 
26251     {
26252         var position = new google.maps.LatLng(this.latitude, this.longitude);
26253         
26254         var _map = new google.maps.Map(this.el.dom, {
26255             center: position,
26256             zoom: this.zoom,
26257             mapTypeId: this.mapTypeId,
26258             mapTypeControl: this.mapTypeControl,
26259             disableDoubleClickZoom: this.disableDoubleClickZoom,
26260             scrollwheel: this.scrollwheel,
26261             streetViewControl: this.streetViewControl,
26262             locationName: this.locationName,
26263             draggable: this.draggable,
26264             enableAutocomplete: this.enableAutocomplete,
26265             enableReverseGeocode: this.enableReverseGeocode
26266         });
26267         
26268         var _marker = new google.maps.Marker({
26269             position: position,
26270             map: _map,
26271             title: this.markerTitle,
26272             draggable: this.draggable
26273         });
26274         
26275         return {
26276             map: _map,
26277             marker: _marker,
26278             circle: null,
26279             location: position,
26280             radius: this.radius,
26281             locationName: this.locationName,
26282             addressComponents: {
26283                 formatted_address: null,
26284                 addressLine1: null,
26285                 addressLine2: null,
26286                 streetName: null,
26287                 streetNumber: null,
26288                 city: null,
26289                 district: null,
26290                 state: null,
26291                 stateOrProvince: null
26292             },
26293             settings: this,
26294             domContainer: this.el.dom,
26295             geodecoder: new google.maps.Geocoder()
26296         };
26297     },
26298     
26299     drawCircle: function(center, radius, options) 
26300     {
26301         if (this.gMapContext.circle != null) {
26302             this.gMapContext.circle.setMap(null);
26303         }
26304         if (radius > 0) {
26305             radius *= 1;
26306             options = Roo.apply({}, options, {
26307                 strokeColor: "#0000FF",
26308                 strokeOpacity: .35,
26309                 strokeWeight: 2,
26310                 fillColor: "#0000FF",
26311                 fillOpacity: .2
26312             });
26313             
26314             options.map = this.gMapContext.map;
26315             options.radius = radius;
26316             options.center = center;
26317             this.gMapContext.circle = new google.maps.Circle(options);
26318             return this.gMapContext.circle;
26319         }
26320         
26321         return null;
26322     },
26323     
26324     setPosition: function(location) 
26325     {
26326         this.gMapContext.location = location;
26327         this.gMapContext.marker.setPosition(location);
26328         this.gMapContext.map.panTo(location);
26329         this.drawCircle(location, this.gMapContext.radius, {});
26330         
26331         var _this = this;
26332         
26333         if (this.gMapContext.settings.enableReverseGeocode) {
26334             this.gMapContext.geodecoder.geocode({
26335                 latLng: this.gMapContext.location
26336             }, function(results, status) {
26337                 
26338                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26339                     _this.gMapContext.locationName = results[0].formatted_address;
26340                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26341                     
26342                     _this.fireEvent('positionchanged', this, location);
26343                 }
26344             });
26345             
26346             return;
26347         }
26348         
26349         this.fireEvent('positionchanged', this, location);
26350     },
26351     
26352     resize: function()
26353     {
26354         google.maps.event.trigger(this.gMapContext.map, "resize");
26355         
26356         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26357         
26358         this.fireEvent('resize', this);
26359     },
26360     
26361     setPositionByLatLng: function(latitude, longitude)
26362     {
26363         this.setPosition(new google.maps.LatLng(latitude, longitude));
26364     },
26365     
26366     getCurrentPosition: function() 
26367     {
26368         return {
26369             latitude: this.gMapContext.location.lat(),
26370             longitude: this.gMapContext.location.lng()
26371         };
26372     },
26373     
26374     getAddressName: function() 
26375     {
26376         return this.gMapContext.locationName;
26377     },
26378     
26379     getAddressComponents: function() 
26380     {
26381         return this.gMapContext.addressComponents;
26382     },
26383     
26384     address_component_from_google_geocode: function(address_components) 
26385     {
26386         var result = {};
26387         
26388         for (var i = 0; i < address_components.length; i++) {
26389             var component = address_components[i];
26390             if (component.types.indexOf("postal_code") >= 0) {
26391                 result.postalCode = component.short_name;
26392             } else if (component.types.indexOf("street_number") >= 0) {
26393                 result.streetNumber = component.short_name;
26394             } else if (component.types.indexOf("route") >= 0) {
26395                 result.streetName = component.short_name;
26396             } else if (component.types.indexOf("neighborhood") >= 0) {
26397                 result.city = component.short_name;
26398             } else if (component.types.indexOf("locality") >= 0) {
26399                 result.city = component.short_name;
26400             } else if (component.types.indexOf("sublocality") >= 0) {
26401                 result.district = component.short_name;
26402             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26403                 result.stateOrProvince = component.short_name;
26404             } else if (component.types.indexOf("country") >= 0) {
26405                 result.country = component.short_name;
26406             }
26407         }
26408         
26409         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26410         result.addressLine2 = "";
26411         return result;
26412     },
26413     
26414     setZoomLevel: function(zoom)
26415     {
26416         this.gMapContext.map.setZoom(zoom);
26417     },
26418     
26419     show: function()
26420     {
26421         if(!this.el){
26422             return;
26423         }
26424         
26425         this.el.show();
26426         
26427         this.resize();
26428         
26429         this.fireEvent('show', this);
26430     },
26431     
26432     hide: function()
26433     {
26434         if(!this.el){
26435             return;
26436         }
26437         
26438         this.el.hide();
26439         
26440         this.fireEvent('hide', this);
26441     }
26442     
26443 });
26444
26445 Roo.apply(Roo.bootstrap.LocationPicker, {
26446     
26447     OverlayView : function(map, options)
26448     {
26449         options = options || {};
26450         
26451         this.setMap(map);
26452     }
26453     
26454     
26455 });/*
26456  * - LGPL
26457  *
26458  * Alert
26459  * 
26460  */
26461
26462 /**
26463  * @class Roo.bootstrap.Alert
26464  * @extends Roo.bootstrap.Component
26465  * Bootstrap Alert class
26466  * @cfg {String} title The title of alert
26467  * @cfg {String} html The content of alert
26468  * @cfg {String} weight (  success | info | warning | danger )
26469  * @cfg {String} faicon font-awesomeicon
26470  * 
26471  * @constructor
26472  * Create a new alert
26473  * @param {Object} config The config object
26474  */
26475
26476
26477 Roo.bootstrap.Alert = function(config){
26478     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26479     
26480 };
26481
26482 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26483     
26484     title: '',
26485     html: '',
26486     weight: false,
26487     faicon: false,
26488     
26489     getAutoCreate : function()
26490     {
26491         
26492         var cfg = {
26493             tag : 'div',
26494             cls : 'alert',
26495             cn : [
26496                 {
26497                     tag : 'i',
26498                     cls : 'roo-alert-icon'
26499                     
26500                 },
26501                 {
26502                     tag : 'b',
26503                     cls : 'roo-alert-title',
26504                     html : this.title
26505                 },
26506                 {
26507                     tag : 'span',
26508                     cls : 'roo-alert-text',
26509                     html : this.html
26510                 }
26511             ]
26512         };
26513         
26514         if(this.faicon){
26515             cfg.cn[0].cls += ' fa ' + this.faicon;
26516         }
26517         
26518         if(this.weight){
26519             cfg.cls += ' alert-' + this.weight;
26520         }
26521         
26522         return cfg;
26523     },
26524     
26525     initEvents: function() 
26526     {
26527         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26528     },
26529     
26530     setTitle : function(str)
26531     {
26532         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26533     },
26534     
26535     setText : function(str)
26536     {
26537         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26538     },
26539     
26540     setWeight : function(weight)
26541     {
26542         if(this.weight){
26543             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26544         }
26545         
26546         this.weight = weight;
26547         
26548         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26549     },
26550     
26551     setIcon : function(icon)
26552     {
26553         if(this.faicon){
26554             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26555         }
26556         
26557         this.faicon = icon;
26558         
26559         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26560     },
26561     
26562     hide: function() 
26563     {
26564         this.el.hide();   
26565     },
26566     
26567     show: function() 
26568     {  
26569         this.el.show();   
26570     }
26571     
26572 });
26573
26574  
26575 /*
26576 * Licence: LGPL
26577 */
26578
26579 /**
26580  * @class Roo.bootstrap.UploadCropbox
26581  * @extends Roo.bootstrap.Component
26582  * Bootstrap UploadCropbox class
26583  * @cfg {String} emptyText show when image has been loaded
26584  * @cfg {String} rotateNotify show when image too small to rotate
26585  * @cfg {Number} errorTimeout default 3000
26586  * @cfg {Number} minWidth default 300
26587  * @cfg {Number} minHeight default 300
26588  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26589  * @cfg {Boolean} isDocument (true|false) default false
26590  * @cfg {String} url action url
26591  * @cfg {String} paramName default 'imageUpload'
26592  * @cfg {String} method default POST
26593  * @cfg {Boolean} loadMask (true|false) default true
26594  * @cfg {Boolean} loadingText default 'Loading...'
26595  * 
26596  * @constructor
26597  * Create a new UploadCropbox
26598  * @param {Object} config The config object
26599  */
26600
26601 Roo.bootstrap.UploadCropbox = function(config){
26602     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26603     
26604     this.addEvents({
26605         /**
26606          * @event beforeselectfile
26607          * Fire before select file
26608          * @param {Roo.bootstrap.UploadCropbox} this
26609          */
26610         "beforeselectfile" : true,
26611         /**
26612          * @event initial
26613          * Fire after initEvent
26614          * @param {Roo.bootstrap.UploadCropbox} this
26615          */
26616         "initial" : true,
26617         /**
26618          * @event crop
26619          * Fire after initEvent
26620          * @param {Roo.bootstrap.UploadCropbox} this
26621          * @param {String} data
26622          */
26623         "crop" : true,
26624         /**
26625          * @event prepare
26626          * Fire when preparing the file data
26627          * @param {Roo.bootstrap.UploadCropbox} this
26628          * @param {Object} file
26629          */
26630         "prepare" : true,
26631         /**
26632          * @event exception
26633          * Fire when get exception
26634          * @param {Roo.bootstrap.UploadCropbox} this
26635          * @param {XMLHttpRequest} xhr
26636          */
26637         "exception" : true,
26638         /**
26639          * @event beforeloadcanvas
26640          * Fire before load the canvas
26641          * @param {Roo.bootstrap.UploadCropbox} this
26642          * @param {String} src
26643          */
26644         "beforeloadcanvas" : true,
26645         /**
26646          * @event trash
26647          * Fire when trash image
26648          * @param {Roo.bootstrap.UploadCropbox} this
26649          */
26650         "trash" : true,
26651         /**
26652          * @event download
26653          * Fire when download the image
26654          * @param {Roo.bootstrap.UploadCropbox} this
26655          */
26656         "download" : true,
26657         /**
26658          * @event footerbuttonclick
26659          * Fire when footerbuttonclick
26660          * @param {Roo.bootstrap.UploadCropbox} this
26661          * @param {String} type
26662          */
26663         "footerbuttonclick" : true,
26664         /**
26665          * @event resize
26666          * Fire when resize
26667          * @param {Roo.bootstrap.UploadCropbox} this
26668          */
26669         "resize" : true,
26670         /**
26671          * @event rotate
26672          * Fire when rotate the image
26673          * @param {Roo.bootstrap.UploadCropbox} this
26674          * @param {String} pos
26675          */
26676         "rotate" : true,
26677         /**
26678          * @event inspect
26679          * Fire when inspect the file
26680          * @param {Roo.bootstrap.UploadCropbox} this
26681          * @param {Object} file
26682          */
26683         "inspect" : true,
26684         /**
26685          * @event upload
26686          * Fire when xhr upload the file
26687          * @param {Roo.bootstrap.UploadCropbox} this
26688          * @param {Object} data
26689          */
26690         "upload" : true,
26691         /**
26692          * @event arrange
26693          * Fire when arrange the file data
26694          * @param {Roo.bootstrap.UploadCropbox} this
26695          * @param {Object} formData
26696          */
26697         "arrange" : true
26698     });
26699     
26700     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26701 };
26702
26703 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26704     
26705     emptyText : 'Click to upload image',
26706     rotateNotify : 'Image is too small to rotate',
26707     errorTimeout : 3000,
26708     scale : 0,
26709     baseScale : 1,
26710     rotate : 0,
26711     dragable : false,
26712     pinching : false,
26713     mouseX : 0,
26714     mouseY : 0,
26715     cropData : false,
26716     minWidth : 300,
26717     minHeight : 300,
26718     file : false,
26719     exif : {},
26720     baseRotate : 1,
26721     cropType : 'image/jpeg',
26722     buttons : false,
26723     canvasLoaded : false,
26724     isDocument : false,
26725     method : 'POST',
26726     paramName : 'imageUpload',
26727     loadMask : true,
26728     loadingText : 'Loading...',
26729     maskEl : false,
26730     
26731     getAutoCreate : function()
26732     {
26733         var cfg = {
26734             tag : 'div',
26735             cls : 'roo-upload-cropbox',
26736             cn : [
26737                 {
26738                     tag : 'input',
26739                     cls : 'roo-upload-cropbox-selector',
26740                     type : 'file'
26741                 },
26742                 {
26743                     tag : 'div',
26744                     cls : 'roo-upload-cropbox-body',
26745                     style : 'cursor:pointer',
26746                     cn : [
26747                         {
26748                             tag : 'div',
26749                             cls : 'roo-upload-cropbox-preview'
26750                         },
26751                         {
26752                             tag : 'div',
26753                             cls : 'roo-upload-cropbox-thumb'
26754                         },
26755                         {
26756                             tag : 'div',
26757                             cls : 'roo-upload-cropbox-empty-notify',
26758                             html : this.emptyText
26759                         },
26760                         {
26761                             tag : 'div',
26762                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26763                             html : this.rotateNotify
26764                         }
26765                     ]
26766                 },
26767                 {
26768                     tag : 'div',
26769                     cls : 'roo-upload-cropbox-footer',
26770                     cn : {
26771                         tag : 'div',
26772                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26773                         cn : []
26774                     }
26775                 }
26776             ]
26777         };
26778         
26779         return cfg;
26780     },
26781     
26782     onRender : function(ct, position)
26783     {
26784         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26785         
26786         if (this.buttons.length) {
26787             
26788             Roo.each(this.buttons, function(bb) {
26789                 
26790                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26791                 
26792                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26793                 
26794             }, this);
26795         }
26796         
26797         if(this.loadMask){
26798             this.maskEl = this.el;
26799         }
26800     },
26801     
26802     initEvents : function()
26803     {
26804         this.urlAPI = (window.createObjectURL && window) || 
26805                                 (window.URL && URL.revokeObjectURL && URL) || 
26806                                 (window.webkitURL && webkitURL);
26807                         
26808         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26809         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26810         
26811         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26812         this.selectorEl.hide();
26813         
26814         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26815         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26816         
26817         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26818         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26819         this.thumbEl.hide();
26820         
26821         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26822         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26823         
26824         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26825         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26826         this.errorEl.hide();
26827         
26828         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26829         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26830         this.footerEl.hide();
26831         
26832         this.setThumbBoxSize();
26833         
26834         this.bind();
26835         
26836         this.resize();
26837         
26838         this.fireEvent('initial', this);
26839     },
26840
26841     bind : function()
26842     {
26843         var _this = this;
26844         
26845         window.addEventListener("resize", function() { _this.resize(); } );
26846         
26847         this.bodyEl.on('click', this.beforeSelectFile, this);
26848         
26849         if(Roo.isTouch){
26850             this.bodyEl.on('touchstart', this.onTouchStart, this);
26851             this.bodyEl.on('touchmove', this.onTouchMove, this);
26852             this.bodyEl.on('touchend', this.onTouchEnd, this);
26853         }
26854         
26855         if(!Roo.isTouch){
26856             this.bodyEl.on('mousedown', this.onMouseDown, this);
26857             this.bodyEl.on('mousemove', this.onMouseMove, this);
26858             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26859             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26860             Roo.get(document).on('mouseup', this.onMouseUp, this);
26861         }
26862         
26863         this.selectorEl.on('change', this.onFileSelected, this);
26864     },
26865     
26866     reset : function()
26867     {    
26868         this.scale = 0;
26869         this.baseScale = 1;
26870         this.rotate = 0;
26871         this.baseRotate = 1;
26872         this.dragable = false;
26873         this.pinching = false;
26874         this.mouseX = 0;
26875         this.mouseY = 0;
26876         this.cropData = false;
26877         this.notifyEl.dom.innerHTML = this.emptyText;
26878         
26879         this.selectorEl.dom.value = '';
26880         
26881     },
26882     
26883     resize : function()
26884     {
26885         if(this.fireEvent('resize', this) != false){
26886             this.setThumbBoxPosition();
26887             this.setCanvasPosition();
26888         }
26889     },
26890     
26891     onFooterButtonClick : function(e, el, o, type)
26892     {
26893         switch (type) {
26894             case 'rotate-left' :
26895                 this.onRotateLeft(e);
26896                 break;
26897             case 'rotate-right' :
26898                 this.onRotateRight(e);
26899                 break;
26900             case 'picture' :
26901                 this.beforeSelectFile(e);
26902                 break;
26903             case 'trash' :
26904                 this.trash(e);
26905                 break;
26906             case 'crop' :
26907                 this.crop(e);
26908                 break;
26909             case 'download' :
26910                 this.download(e);
26911                 break;
26912             default :
26913                 break;
26914         }
26915         
26916         this.fireEvent('footerbuttonclick', this, type);
26917     },
26918     
26919     beforeSelectFile : function(e)
26920     {
26921         e.preventDefault();
26922         
26923         if(this.fireEvent('beforeselectfile', this) != false){
26924             this.selectorEl.dom.click();
26925         }
26926     },
26927     
26928     onFileSelected : function(e)
26929     {
26930         e.preventDefault();
26931         
26932         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26933             return;
26934         }
26935         
26936         var file = this.selectorEl.dom.files[0];
26937         
26938         if(this.fireEvent('inspect', this, file) != false){
26939             this.prepare(file);
26940         }
26941         
26942     },
26943     
26944     trash : function(e)
26945     {
26946         this.fireEvent('trash', this);
26947     },
26948     
26949     download : function(e)
26950     {
26951         this.fireEvent('download', this);
26952     },
26953     
26954     loadCanvas : function(src)
26955     {   
26956         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26957             
26958             this.reset();
26959             
26960             this.imageEl = document.createElement('img');
26961             
26962             var _this = this;
26963             
26964             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26965             
26966             this.imageEl.src = src;
26967         }
26968     },
26969     
26970     onLoadCanvas : function()
26971     {   
26972         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26973         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26974         
26975         this.bodyEl.un('click', this.beforeSelectFile, this);
26976         
26977         this.notifyEl.hide();
26978         this.thumbEl.show();
26979         this.footerEl.show();
26980         
26981         this.baseRotateLevel();
26982         
26983         if(this.isDocument){
26984             this.setThumbBoxSize();
26985         }
26986         
26987         this.setThumbBoxPosition();
26988         
26989         this.baseScaleLevel();
26990         
26991         this.draw();
26992         
26993         this.resize();
26994         
26995         this.canvasLoaded = true;
26996         
26997         if(this.loadMask){
26998             this.maskEl.unmask();
26999         }
27000         
27001     },
27002     
27003     setCanvasPosition : function()
27004     {   
27005         if(!this.canvasEl){
27006             return;
27007         }
27008         
27009         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27010         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27011         
27012         this.previewEl.setLeft(pw);
27013         this.previewEl.setTop(ph);
27014         
27015     },
27016     
27017     onMouseDown : function(e)
27018     {   
27019         e.stopEvent();
27020         
27021         this.dragable = true;
27022         this.pinching = false;
27023         
27024         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27025             this.dragable = false;
27026             return;
27027         }
27028         
27029         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27030         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27031         
27032     },
27033     
27034     onMouseMove : function(e)
27035     {   
27036         e.stopEvent();
27037         
27038         if(!this.canvasLoaded){
27039             return;
27040         }
27041         
27042         if (!this.dragable){
27043             return;
27044         }
27045         
27046         var minX = Math.ceil(this.thumbEl.getLeft(true));
27047         var minY = Math.ceil(this.thumbEl.getTop(true));
27048         
27049         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27050         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27051         
27052         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27053         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27054         
27055         x = x - this.mouseX;
27056         y = y - this.mouseY;
27057         
27058         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27059         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27060         
27061         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27062         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27063         
27064         this.previewEl.setLeft(bgX);
27065         this.previewEl.setTop(bgY);
27066         
27067         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27068         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27069     },
27070     
27071     onMouseUp : function(e)
27072     {   
27073         e.stopEvent();
27074         
27075         this.dragable = false;
27076     },
27077     
27078     onMouseWheel : function(e)
27079     {   
27080         e.stopEvent();
27081         
27082         this.startScale = this.scale;
27083         
27084         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27085         
27086         if(!this.zoomable()){
27087             this.scale = this.startScale;
27088             return;
27089         }
27090         
27091         this.draw();
27092         
27093         return;
27094     },
27095     
27096     zoomable : function()
27097     {
27098         var minScale = this.thumbEl.getWidth() / this.minWidth;
27099         
27100         if(this.minWidth < this.minHeight){
27101             minScale = this.thumbEl.getHeight() / this.minHeight;
27102         }
27103         
27104         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27105         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27106         
27107         if(
27108                 this.isDocument &&
27109                 (this.rotate == 0 || this.rotate == 180) && 
27110                 (
27111                     width > this.imageEl.OriginWidth || 
27112                     height > this.imageEl.OriginHeight ||
27113                     (width < this.minWidth && height < this.minHeight)
27114                 )
27115         ){
27116             return false;
27117         }
27118         
27119         if(
27120                 this.isDocument &&
27121                 (this.rotate == 90 || this.rotate == 270) && 
27122                 (
27123                     width > this.imageEl.OriginWidth || 
27124                     height > this.imageEl.OriginHeight ||
27125                     (width < this.minHeight && height < this.minWidth)
27126                 )
27127         ){
27128             return false;
27129         }
27130         
27131         if(
27132                 !this.isDocument &&
27133                 (this.rotate == 0 || this.rotate == 180) && 
27134                 (
27135                     width < this.minWidth || 
27136                     width > this.imageEl.OriginWidth || 
27137                     height < this.minHeight || 
27138                     height > this.imageEl.OriginHeight
27139                 )
27140         ){
27141             return false;
27142         }
27143         
27144         if(
27145                 !this.isDocument &&
27146                 (this.rotate == 90 || this.rotate == 270) && 
27147                 (
27148                     width < this.minHeight || 
27149                     width > this.imageEl.OriginWidth || 
27150                     height < this.minWidth || 
27151                     height > this.imageEl.OriginHeight
27152                 )
27153         ){
27154             return false;
27155         }
27156         
27157         return true;
27158         
27159     },
27160     
27161     onRotateLeft : function(e)
27162     {   
27163         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27164             
27165             var minScale = this.thumbEl.getWidth() / this.minWidth;
27166             
27167             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27168             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27169             
27170             this.startScale = this.scale;
27171             
27172             while (this.getScaleLevel() < minScale){
27173             
27174                 this.scale = this.scale + 1;
27175                 
27176                 if(!this.zoomable()){
27177                     break;
27178                 }
27179                 
27180                 if(
27181                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27182                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27183                 ){
27184                     continue;
27185                 }
27186                 
27187                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27188
27189                 this.draw();
27190                 
27191                 return;
27192             }
27193             
27194             this.scale = this.startScale;
27195             
27196             this.onRotateFail();
27197             
27198             return false;
27199         }
27200         
27201         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27202
27203         if(this.isDocument){
27204             this.setThumbBoxSize();
27205             this.setThumbBoxPosition();
27206             this.setCanvasPosition();
27207         }
27208         
27209         this.draw();
27210         
27211         this.fireEvent('rotate', this, 'left');
27212         
27213     },
27214     
27215     onRotateRight : function(e)
27216     {
27217         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27218             
27219             var minScale = this.thumbEl.getWidth() / this.minWidth;
27220         
27221             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27222             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27223             
27224             this.startScale = this.scale;
27225             
27226             while (this.getScaleLevel() < minScale){
27227             
27228                 this.scale = this.scale + 1;
27229                 
27230                 if(!this.zoomable()){
27231                     break;
27232                 }
27233                 
27234                 if(
27235                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27236                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27237                 ){
27238                     continue;
27239                 }
27240                 
27241                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27242
27243                 this.draw();
27244                 
27245                 return;
27246             }
27247             
27248             this.scale = this.startScale;
27249             
27250             this.onRotateFail();
27251             
27252             return false;
27253         }
27254         
27255         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27256
27257         if(this.isDocument){
27258             this.setThumbBoxSize();
27259             this.setThumbBoxPosition();
27260             this.setCanvasPosition();
27261         }
27262         
27263         this.draw();
27264         
27265         this.fireEvent('rotate', this, 'right');
27266     },
27267     
27268     onRotateFail : function()
27269     {
27270         this.errorEl.show(true);
27271         
27272         var _this = this;
27273         
27274         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27275     },
27276     
27277     draw : function()
27278     {
27279         this.previewEl.dom.innerHTML = '';
27280         
27281         var canvasEl = document.createElement("canvas");
27282         
27283         var contextEl = canvasEl.getContext("2d");
27284         
27285         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27286         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27287         var center = this.imageEl.OriginWidth / 2;
27288         
27289         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27290             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27291             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27292             center = this.imageEl.OriginHeight / 2;
27293         }
27294         
27295         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27296         
27297         contextEl.translate(center, center);
27298         contextEl.rotate(this.rotate * Math.PI / 180);
27299
27300         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27301         
27302         this.canvasEl = document.createElement("canvas");
27303         
27304         this.contextEl = this.canvasEl.getContext("2d");
27305         
27306         switch (this.rotate) {
27307             case 0 :
27308                 
27309                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27310                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27311                 
27312                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27313                 
27314                 break;
27315             case 90 : 
27316                 
27317                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27318                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27319                 
27320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27321                     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);
27322                     break;
27323                 }
27324                 
27325                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27326                 
27327                 break;
27328             case 180 :
27329                 
27330                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27331                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27332                 
27333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27334                     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);
27335                     break;
27336                 }
27337                 
27338                 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);
27339                 
27340                 break;
27341             case 270 :
27342                 
27343                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27344                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27345         
27346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27347                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27348                     break;
27349                 }
27350                 
27351                 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);
27352                 
27353                 break;
27354             default : 
27355                 break;
27356         }
27357         
27358         this.previewEl.appendChild(this.canvasEl);
27359         
27360         this.setCanvasPosition();
27361     },
27362     
27363     crop : function()
27364     {
27365         if(!this.canvasLoaded){
27366             return;
27367         }
27368         
27369         var imageCanvas = document.createElement("canvas");
27370         
27371         var imageContext = imageCanvas.getContext("2d");
27372         
27373         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27374         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27375         
27376         var center = imageCanvas.width / 2;
27377         
27378         imageContext.translate(center, center);
27379         
27380         imageContext.rotate(this.rotate * Math.PI / 180);
27381         
27382         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27383         
27384         var canvas = document.createElement("canvas");
27385         
27386         var context = canvas.getContext("2d");
27387                 
27388         canvas.width = this.minWidth;
27389         canvas.height = this.minHeight;
27390
27391         switch (this.rotate) {
27392             case 0 :
27393                 
27394                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27395                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27396                 
27397                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27398                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27399                 
27400                 var targetWidth = this.minWidth - 2 * x;
27401                 var targetHeight = this.minHeight - 2 * y;
27402                 
27403                 var scale = 1;
27404                 
27405                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27406                     scale = targetWidth / width;
27407                 }
27408                 
27409                 if(x > 0 && y == 0){
27410                     scale = targetHeight / height;
27411                 }
27412                 
27413                 if(x > 0 && y > 0){
27414                     scale = targetWidth / width;
27415                     
27416                     if(width < height){
27417                         scale = targetHeight / height;
27418                     }
27419                 }
27420                 
27421                 context.scale(scale, scale);
27422                 
27423                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27424                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27425
27426                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27427                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27428
27429                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27430                 
27431                 break;
27432             case 90 : 
27433                 
27434                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27435                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27436                 
27437                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27438                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27439                 
27440                 var targetWidth = this.minWidth - 2 * x;
27441                 var targetHeight = this.minHeight - 2 * y;
27442                 
27443                 var scale = 1;
27444                 
27445                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27446                     scale = targetWidth / width;
27447                 }
27448                 
27449                 if(x > 0 && y == 0){
27450                     scale = targetHeight / height;
27451                 }
27452                 
27453                 if(x > 0 && y > 0){
27454                     scale = targetWidth / width;
27455                     
27456                     if(width < height){
27457                         scale = targetHeight / height;
27458                     }
27459                 }
27460                 
27461                 context.scale(scale, scale);
27462                 
27463                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27464                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27465
27466                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27467                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27468                 
27469                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27470                 
27471                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27472                 
27473                 break;
27474             case 180 :
27475                 
27476                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27477                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27478                 
27479                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27480                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27481                 
27482                 var targetWidth = this.minWidth - 2 * x;
27483                 var targetHeight = this.minHeight - 2 * y;
27484                 
27485                 var scale = 1;
27486                 
27487                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27488                     scale = targetWidth / width;
27489                 }
27490                 
27491                 if(x > 0 && y == 0){
27492                     scale = targetHeight / height;
27493                 }
27494                 
27495                 if(x > 0 && y > 0){
27496                     scale = targetWidth / width;
27497                     
27498                     if(width < height){
27499                         scale = targetHeight / height;
27500                     }
27501                 }
27502                 
27503                 context.scale(scale, scale);
27504                 
27505                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27506                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27507
27508                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27509                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27510
27511                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27512                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27513                 
27514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27515                 
27516                 break;
27517             case 270 :
27518                 
27519                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27520                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27521                 
27522                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27523                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27524                 
27525                 var targetWidth = this.minWidth - 2 * x;
27526                 var targetHeight = this.minHeight - 2 * y;
27527                 
27528                 var scale = 1;
27529                 
27530                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27531                     scale = targetWidth / width;
27532                 }
27533                 
27534                 if(x > 0 && y == 0){
27535                     scale = targetHeight / height;
27536                 }
27537                 
27538                 if(x > 0 && y > 0){
27539                     scale = targetWidth / width;
27540                     
27541                     if(width < height){
27542                         scale = targetHeight / height;
27543                     }
27544                 }
27545                 
27546                 context.scale(scale, scale);
27547                 
27548                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27549                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27550
27551                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27552                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27553                 
27554                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27555                 
27556                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27557                 
27558                 break;
27559             default : 
27560                 break;
27561         }
27562         
27563         this.cropData = canvas.toDataURL(this.cropType);
27564         
27565         if(this.fireEvent('crop', this, this.cropData) !== false){
27566             this.process(this.file, this.cropData);
27567         }
27568         
27569         return;
27570         
27571     },
27572     
27573     setThumbBoxSize : function()
27574     {
27575         var width, height;
27576         
27577         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27578             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27579             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27580             
27581             this.minWidth = width;
27582             this.minHeight = height;
27583             
27584             if(this.rotate == 90 || this.rotate == 270){
27585                 this.minWidth = height;
27586                 this.minHeight = width;
27587             }
27588         }
27589         
27590         height = 300;
27591         width = Math.ceil(this.minWidth * height / this.minHeight);
27592         
27593         if(this.minWidth > this.minHeight){
27594             width = 300;
27595             height = Math.ceil(this.minHeight * width / this.minWidth);
27596         }
27597         
27598         this.thumbEl.setStyle({
27599             width : width + 'px',
27600             height : height + 'px'
27601         });
27602
27603         return;
27604             
27605     },
27606     
27607     setThumbBoxPosition : function()
27608     {
27609         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27610         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27611         
27612         this.thumbEl.setLeft(x);
27613         this.thumbEl.setTop(y);
27614         
27615     },
27616     
27617     baseRotateLevel : function()
27618     {
27619         this.baseRotate = 1;
27620         
27621         if(
27622                 typeof(this.exif) != 'undefined' &&
27623                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27624                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27625         ){
27626             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27627         }
27628         
27629         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27630         
27631     },
27632     
27633     baseScaleLevel : function()
27634     {
27635         var width, height;
27636         
27637         if(this.isDocument){
27638             
27639             if(this.baseRotate == 6 || this.baseRotate == 8){
27640             
27641                 height = this.thumbEl.getHeight();
27642                 this.baseScale = height / this.imageEl.OriginWidth;
27643
27644                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27645                     width = this.thumbEl.getWidth();
27646                     this.baseScale = width / this.imageEl.OriginHeight;
27647                 }
27648
27649                 return;
27650             }
27651
27652             height = this.thumbEl.getHeight();
27653             this.baseScale = height / this.imageEl.OriginHeight;
27654
27655             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27656                 width = this.thumbEl.getWidth();
27657                 this.baseScale = width / this.imageEl.OriginWidth;
27658             }
27659
27660             return;
27661         }
27662         
27663         if(this.baseRotate == 6 || this.baseRotate == 8){
27664             
27665             width = this.thumbEl.getHeight();
27666             this.baseScale = width / this.imageEl.OriginHeight;
27667             
27668             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27669                 height = this.thumbEl.getWidth();
27670                 this.baseScale = height / this.imageEl.OriginHeight;
27671             }
27672             
27673             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27674                 height = this.thumbEl.getWidth();
27675                 this.baseScale = height / this.imageEl.OriginHeight;
27676                 
27677                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27678                     width = this.thumbEl.getHeight();
27679                     this.baseScale = width / this.imageEl.OriginWidth;
27680                 }
27681             }
27682             
27683             return;
27684         }
27685         
27686         width = this.thumbEl.getWidth();
27687         this.baseScale = width / this.imageEl.OriginWidth;
27688         
27689         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27690             height = this.thumbEl.getHeight();
27691             this.baseScale = height / this.imageEl.OriginHeight;
27692         }
27693         
27694         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27695             
27696             height = this.thumbEl.getHeight();
27697             this.baseScale = height / this.imageEl.OriginHeight;
27698             
27699             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27700                 width = this.thumbEl.getWidth();
27701                 this.baseScale = width / this.imageEl.OriginWidth;
27702             }
27703             
27704         }
27705         
27706         return;
27707     },
27708     
27709     getScaleLevel : function()
27710     {
27711         return this.baseScale * Math.pow(1.1, this.scale);
27712     },
27713     
27714     onTouchStart : function(e)
27715     {
27716         if(!this.canvasLoaded){
27717             this.beforeSelectFile(e);
27718             return;
27719         }
27720         
27721         var touches = e.browserEvent.touches;
27722         
27723         if(!touches){
27724             return;
27725         }
27726         
27727         if(touches.length == 1){
27728             this.onMouseDown(e);
27729             return;
27730         }
27731         
27732         if(touches.length != 2){
27733             return;
27734         }
27735         
27736         var coords = [];
27737         
27738         for(var i = 0, finger; finger = touches[i]; i++){
27739             coords.push(finger.pageX, finger.pageY);
27740         }
27741         
27742         var x = Math.pow(coords[0] - coords[2], 2);
27743         var y = Math.pow(coords[1] - coords[3], 2);
27744         
27745         this.startDistance = Math.sqrt(x + y);
27746         
27747         this.startScale = this.scale;
27748         
27749         this.pinching = true;
27750         this.dragable = false;
27751         
27752     },
27753     
27754     onTouchMove : function(e)
27755     {
27756         if(!this.pinching && !this.dragable){
27757             return;
27758         }
27759         
27760         var touches = e.browserEvent.touches;
27761         
27762         if(!touches){
27763             return;
27764         }
27765         
27766         if(this.dragable){
27767             this.onMouseMove(e);
27768             return;
27769         }
27770         
27771         var coords = [];
27772         
27773         for(var i = 0, finger; finger = touches[i]; i++){
27774             coords.push(finger.pageX, finger.pageY);
27775         }
27776         
27777         var x = Math.pow(coords[0] - coords[2], 2);
27778         var y = Math.pow(coords[1] - coords[3], 2);
27779         
27780         this.endDistance = Math.sqrt(x + y);
27781         
27782         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27783         
27784         if(!this.zoomable()){
27785             this.scale = this.startScale;
27786             return;
27787         }
27788         
27789         this.draw();
27790         
27791     },
27792     
27793     onTouchEnd : function(e)
27794     {
27795         this.pinching = false;
27796         this.dragable = false;
27797         
27798     },
27799     
27800     process : function(file, crop)
27801     {
27802         if(this.loadMask){
27803             this.maskEl.mask(this.loadingText);
27804         }
27805         
27806         this.xhr = new XMLHttpRequest();
27807         
27808         file.xhr = this.xhr;
27809
27810         this.xhr.open(this.method, this.url, true);
27811         
27812         var headers = {
27813             "Accept": "application/json",
27814             "Cache-Control": "no-cache",
27815             "X-Requested-With": "XMLHttpRequest"
27816         };
27817         
27818         for (var headerName in headers) {
27819             var headerValue = headers[headerName];
27820             if (headerValue) {
27821                 this.xhr.setRequestHeader(headerName, headerValue);
27822             }
27823         }
27824         
27825         var _this = this;
27826         
27827         this.xhr.onload = function()
27828         {
27829             _this.xhrOnLoad(_this.xhr);
27830         }
27831         
27832         this.xhr.onerror = function()
27833         {
27834             _this.xhrOnError(_this.xhr);
27835         }
27836         
27837         var formData = new FormData();
27838
27839         formData.append('returnHTML', 'NO');
27840         
27841         if(crop){
27842             formData.append('crop', crop);
27843         }
27844         
27845         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27846             formData.append(this.paramName, file, file.name);
27847         }
27848         
27849         if(typeof(file.filename) != 'undefined'){
27850             formData.append('filename', file.filename);
27851         }
27852         
27853         if(typeof(file.mimetype) != 'undefined'){
27854             formData.append('mimetype', file.mimetype);
27855         }
27856         
27857         if(this.fireEvent('arrange', this, formData) != false){
27858             this.xhr.send(formData);
27859         };
27860     },
27861     
27862     xhrOnLoad : function(xhr)
27863     {
27864         if(this.loadMask){
27865             this.maskEl.unmask();
27866         }
27867         
27868         if (xhr.readyState !== 4) {
27869             this.fireEvent('exception', this, xhr);
27870             return;
27871         }
27872
27873         var response = Roo.decode(xhr.responseText);
27874         
27875         if(!response.success){
27876             this.fireEvent('exception', this, xhr);
27877             return;
27878         }
27879         
27880         var response = Roo.decode(xhr.responseText);
27881         
27882         this.fireEvent('upload', this, response);
27883         
27884     },
27885     
27886     xhrOnError : function()
27887     {
27888         if(this.loadMask){
27889             this.maskEl.unmask();
27890         }
27891         
27892         Roo.log('xhr on error');
27893         
27894         var response = Roo.decode(xhr.responseText);
27895           
27896         Roo.log(response);
27897         
27898     },
27899     
27900     prepare : function(file)
27901     {   
27902         if(this.loadMask){
27903             this.maskEl.mask(this.loadingText);
27904         }
27905         
27906         this.file = false;
27907         this.exif = {};
27908         
27909         if(typeof(file) === 'string'){
27910             this.loadCanvas(file);
27911             return;
27912         }
27913         
27914         if(!file || !this.urlAPI){
27915             return;
27916         }
27917         
27918         this.file = file;
27919         this.cropType = file.type;
27920         
27921         var _this = this;
27922         
27923         if(this.fireEvent('prepare', this, this.file) != false){
27924             
27925             var reader = new FileReader();
27926             
27927             reader.onload = function (e) {
27928                 if (e.target.error) {
27929                     Roo.log(e.target.error);
27930                     return;
27931                 }
27932                 
27933                 var buffer = e.target.result,
27934                     dataView = new DataView(buffer),
27935                     offset = 2,
27936                     maxOffset = dataView.byteLength - 4,
27937                     markerBytes,
27938                     markerLength;
27939                 
27940                 if (dataView.getUint16(0) === 0xffd8) {
27941                     while (offset < maxOffset) {
27942                         markerBytes = dataView.getUint16(offset);
27943                         
27944                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27945                             markerLength = dataView.getUint16(offset + 2) + 2;
27946                             if (offset + markerLength > dataView.byteLength) {
27947                                 Roo.log('Invalid meta data: Invalid segment size.');
27948                                 break;
27949                             }
27950                             
27951                             if(markerBytes == 0xffe1){
27952                                 _this.parseExifData(
27953                                     dataView,
27954                                     offset,
27955                                     markerLength
27956                                 );
27957                             }
27958                             
27959                             offset += markerLength;
27960                             
27961                             continue;
27962                         }
27963                         
27964                         break;
27965                     }
27966                     
27967                 }
27968                 
27969                 var url = _this.urlAPI.createObjectURL(_this.file);
27970                 
27971                 _this.loadCanvas(url);
27972                 
27973                 return;
27974             }
27975             
27976             reader.readAsArrayBuffer(this.file);
27977             
27978         }
27979         
27980     },
27981     
27982     parseExifData : function(dataView, offset, length)
27983     {
27984         var tiffOffset = offset + 10,
27985             littleEndian,
27986             dirOffset;
27987     
27988         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27989             // No Exif data, might be XMP data instead
27990             return;
27991         }
27992         
27993         // Check for the ASCII code for "Exif" (0x45786966):
27994         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27995             // No Exif data, might be XMP data instead
27996             return;
27997         }
27998         if (tiffOffset + 8 > dataView.byteLength) {
27999             Roo.log('Invalid Exif data: Invalid segment size.');
28000             return;
28001         }
28002         // Check for the two null bytes:
28003         if (dataView.getUint16(offset + 8) !== 0x0000) {
28004             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28005             return;
28006         }
28007         // Check the byte alignment:
28008         switch (dataView.getUint16(tiffOffset)) {
28009         case 0x4949:
28010             littleEndian = true;
28011             break;
28012         case 0x4D4D:
28013             littleEndian = false;
28014             break;
28015         default:
28016             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28017             return;
28018         }
28019         // Check for the TIFF tag marker (0x002A):
28020         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28021             Roo.log('Invalid Exif data: Missing TIFF marker.');
28022             return;
28023         }
28024         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28025         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28026         
28027         this.parseExifTags(
28028             dataView,
28029             tiffOffset,
28030             tiffOffset + dirOffset,
28031             littleEndian
28032         );
28033     },
28034     
28035     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28036     {
28037         var tagsNumber,
28038             dirEndOffset,
28039             i;
28040         if (dirOffset + 6 > dataView.byteLength) {
28041             Roo.log('Invalid Exif data: Invalid directory offset.');
28042             return;
28043         }
28044         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28045         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28046         if (dirEndOffset + 4 > dataView.byteLength) {
28047             Roo.log('Invalid Exif data: Invalid directory size.');
28048             return;
28049         }
28050         for (i = 0; i < tagsNumber; i += 1) {
28051             this.parseExifTag(
28052                 dataView,
28053                 tiffOffset,
28054                 dirOffset + 2 + 12 * i, // tag offset
28055                 littleEndian
28056             );
28057         }
28058         // Return the offset to the next directory:
28059         return dataView.getUint32(dirEndOffset, littleEndian);
28060     },
28061     
28062     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28063     {
28064         var tag = dataView.getUint16(offset, littleEndian);
28065         
28066         this.exif[tag] = this.getExifValue(
28067             dataView,
28068             tiffOffset,
28069             offset,
28070             dataView.getUint16(offset + 2, littleEndian), // tag type
28071             dataView.getUint32(offset + 4, littleEndian), // tag length
28072             littleEndian
28073         );
28074     },
28075     
28076     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28077     {
28078         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28079             tagSize,
28080             dataOffset,
28081             values,
28082             i,
28083             str,
28084             c;
28085     
28086         if (!tagType) {
28087             Roo.log('Invalid Exif data: Invalid tag type.');
28088             return;
28089         }
28090         
28091         tagSize = tagType.size * length;
28092         // Determine if the value is contained in the dataOffset bytes,
28093         // or if the value at the dataOffset is a pointer to the actual data:
28094         dataOffset = tagSize > 4 ?
28095                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28096         if (dataOffset + tagSize > dataView.byteLength) {
28097             Roo.log('Invalid Exif data: Invalid data offset.');
28098             return;
28099         }
28100         if (length === 1) {
28101             return tagType.getValue(dataView, dataOffset, littleEndian);
28102         }
28103         values = [];
28104         for (i = 0; i < length; i += 1) {
28105             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28106         }
28107         
28108         if (tagType.ascii) {
28109             str = '';
28110             // Concatenate the chars:
28111             for (i = 0; i < values.length; i += 1) {
28112                 c = values[i];
28113                 // Ignore the terminating NULL byte(s):
28114                 if (c === '\u0000') {
28115                     break;
28116                 }
28117                 str += c;
28118             }
28119             return str;
28120         }
28121         return values;
28122     }
28123     
28124 });
28125
28126 Roo.apply(Roo.bootstrap.UploadCropbox, {
28127     tags : {
28128         'Orientation': 0x0112
28129     },
28130     
28131     Orientation: {
28132             1: 0, //'top-left',
28133 //            2: 'top-right',
28134             3: 180, //'bottom-right',
28135 //            4: 'bottom-left',
28136 //            5: 'left-top',
28137             6: 90, //'right-top',
28138 //            7: 'right-bottom',
28139             8: 270 //'left-bottom'
28140     },
28141     
28142     exifTagTypes : {
28143         // byte, 8-bit unsigned int:
28144         1: {
28145             getValue: function (dataView, dataOffset) {
28146                 return dataView.getUint8(dataOffset);
28147             },
28148             size: 1
28149         },
28150         // ascii, 8-bit byte:
28151         2: {
28152             getValue: function (dataView, dataOffset) {
28153                 return String.fromCharCode(dataView.getUint8(dataOffset));
28154             },
28155             size: 1,
28156             ascii: true
28157         },
28158         // short, 16 bit int:
28159         3: {
28160             getValue: function (dataView, dataOffset, littleEndian) {
28161                 return dataView.getUint16(dataOffset, littleEndian);
28162             },
28163             size: 2
28164         },
28165         // long, 32 bit int:
28166         4: {
28167             getValue: function (dataView, dataOffset, littleEndian) {
28168                 return dataView.getUint32(dataOffset, littleEndian);
28169             },
28170             size: 4
28171         },
28172         // rational = two long values, first is numerator, second is denominator:
28173         5: {
28174             getValue: function (dataView, dataOffset, littleEndian) {
28175                 return dataView.getUint32(dataOffset, littleEndian) /
28176                     dataView.getUint32(dataOffset + 4, littleEndian);
28177             },
28178             size: 8
28179         },
28180         // slong, 32 bit signed int:
28181         9: {
28182             getValue: function (dataView, dataOffset, littleEndian) {
28183                 return dataView.getInt32(dataOffset, littleEndian);
28184             },
28185             size: 4
28186         },
28187         // srational, two slongs, first is numerator, second is denominator:
28188         10: {
28189             getValue: function (dataView, dataOffset, littleEndian) {
28190                 return dataView.getInt32(dataOffset, littleEndian) /
28191                     dataView.getInt32(dataOffset + 4, littleEndian);
28192             },
28193             size: 8
28194         }
28195     },
28196     
28197     footer : {
28198         STANDARD : [
28199             {
28200                 tag : 'div',
28201                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28202                 action : 'rotate-left',
28203                 cn : [
28204                     {
28205                         tag : 'button',
28206                         cls : 'btn btn-default',
28207                         html : '<i class="fa fa-undo"></i>'
28208                     }
28209                 ]
28210             },
28211             {
28212                 tag : 'div',
28213                 cls : 'btn-group roo-upload-cropbox-picture',
28214                 action : 'picture',
28215                 cn : [
28216                     {
28217                         tag : 'button',
28218                         cls : 'btn btn-default',
28219                         html : '<i class="fa fa-picture-o"></i>'
28220                     }
28221                 ]
28222             },
28223             {
28224                 tag : 'div',
28225                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28226                 action : 'rotate-right',
28227                 cn : [
28228                     {
28229                         tag : 'button',
28230                         cls : 'btn btn-default',
28231                         html : '<i class="fa fa-repeat"></i>'
28232                     }
28233                 ]
28234             }
28235         ],
28236         DOCUMENT : [
28237             {
28238                 tag : 'div',
28239                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28240                 action : 'rotate-left',
28241                 cn : [
28242                     {
28243                         tag : 'button',
28244                         cls : 'btn btn-default',
28245                         html : '<i class="fa fa-undo"></i>'
28246                     }
28247                 ]
28248             },
28249             {
28250                 tag : 'div',
28251                 cls : 'btn-group roo-upload-cropbox-download',
28252                 action : 'download',
28253                 cn : [
28254                     {
28255                         tag : 'button',
28256                         cls : 'btn btn-default',
28257                         html : '<i class="fa fa-download"></i>'
28258                     }
28259                 ]
28260             },
28261             {
28262                 tag : 'div',
28263                 cls : 'btn-group roo-upload-cropbox-crop',
28264                 action : 'crop',
28265                 cn : [
28266                     {
28267                         tag : 'button',
28268                         cls : 'btn btn-default',
28269                         html : '<i class="fa fa-crop"></i>'
28270                     }
28271                 ]
28272             },
28273             {
28274                 tag : 'div',
28275                 cls : 'btn-group roo-upload-cropbox-trash',
28276                 action : 'trash',
28277                 cn : [
28278                     {
28279                         tag : 'button',
28280                         cls : 'btn btn-default',
28281                         html : '<i class="fa fa-trash"></i>'
28282                     }
28283                 ]
28284             },
28285             {
28286                 tag : 'div',
28287                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28288                 action : 'rotate-right',
28289                 cn : [
28290                     {
28291                         tag : 'button',
28292                         cls : 'btn btn-default',
28293                         html : '<i class="fa fa-repeat"></i>'
28294                     }
28295                 ]
28296             }
28297         ],
28298         ROTATOR : [
28299             {
28300                 tag : 'div',
28301                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28302                 action : 'rotate-left',
28303                 cn : [
28304                     {
28305                         tag : 'button',
28306                         cls : 'btn btn-default',
28307                         html : '<i class="fa fa-undo"></i>'
28308                     }
28309                 ]
28310             },
28311             {
28312                 tag : 'div',
28313                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28314                 action : 'rotate-right',
28315                 cn : [
28316                     {
28317                         tag : 'button',
28318                         cls : 'btn btn-default',
28319                         html : '<i class="fa fa-repeat"></i>'
28320                     }
28321                 ]
28322             }
28323         ]
28324     }
28325 });
28326
28327 /*
28328 * Licence: LGPL
28329 */
28330
28331 /**
28332  * @class Roo.bootstrap.DocumentManager
28333  * @extends Roo.bootstrap.Component
28334  * Bootstrap DocumentManager class
28335  * @cfg {String} paramName default 'imageUpload'
28336  * @cfg {String} toolTipName default 'filename'
28337  * @cfg {String} method default POST
28338  * @cfg {String} url action url
28339  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28340  * @cfg {Boolean} multiple multiple upload default true
28341  * @cfg {Number} thumbSize default 300
28342  * @cfg {String} fieldLabel
28343  * @cfg {Number} labelWidth default 4
28344  * @cfg {String} labelAlign (left|top) default left
28345  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28346 * @cfg {Number} labellg set the width of label (1-12)
28347  * @cfg {Number} labelmd set the width of label (1-12)
28348  * @cfg {Number} labelsm set the width of label (1-12)
28349  * @cfg {Number} labelxs set the width of label (1-12)
28350  * 
28351  * @constructor
28352  * Create a new DocumentManager
28353  * @param {Object} config The config object
28354  */
28355
28356 Roo.bootstrap.DocumentManager = function(config){
28357     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28358     
28359     this.files = [];
28360     this.delegates = [];
28361     
28362     this.addEvents({
28363         /**
28364          * @event initial
28365          * Fire when initial the DocumentManager
28366          * @param {Roo.bootstrap.DocumentManager} this
28367          */
28368         "initial" : true,
28369         /**
28370          * @event inspect
28371          * inspect selected file
28372          * @param {Roo.bootstrap.DocumentManager} this
28373          * @param {File} file
28374          */
28375         "inspect" : true,
28376         /**
28377          * @event exception
28378          * Fire when xhr load exception
28379          * @param {Roo.bootstrap.DocumentManager} this
28380          * @param {XMLHttpRequest} xhr
28381          */
28382         "exception" : true,
28383         /**
28384          * @event afterupload
28385          * Fire when xhr load exception
28386          * @param {Roo.bootstrap.DocumentManager} this
28387          * @param {XMLHttpRequest} xhr
28388          */
28389         "afterupload" : true,
28390         /**
28391          * @event prepare
28392          * prepare the form data
28393          * @param {Roo.bootstrap.DocumentManager} this
28394          * @param {Object} formData
28395          */
28396         "prepare" : true,
28397         /**
28398          * @event remove
28399          * Fire when remove the file
28400          * @param {Roo.bootstrap.DocumentManager} this
28401          * @param {Object} file
28402          */
28403         "remove" : true,
28404         /**
28405          * @event refresh
28406          * Fire after refresh the file
28407          * @param {Roo.bootstrap.DocumentManager} this
28408          */
28409         "refresh" : true,
28410         /**
28411          * @event click
28412          * Fire after click the image
28413          * @param {Roo.bootstrap.DocumentManager} this
28414          * @param {Object} file
28415          */
28416         "click" : true,
28417         /**
28418          * @event edit
28419          * Fire when upload a image and editable set to true
28420          * @param {Roo.bootstrap.DocumentManager} this
28421          * @param {Object} file
28422          */
28423         "edit" : true,
28424         /**
28425          * @event beforeselectfile
28426          * Fire before select file
28427          * @param {Roo.bootstrap.DocumentManager} this
28428          */
28429         "beforeselectfile" : true,
28430         /**
28431          * @event process
28432          * Fire before process file
28433          * @param {Roo.bootstrap.DocumentManager} this
28434          * @param {Object} file
28435          */
28436         "process" : true
28437         
28438     });
28439 };
28440
28441 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28442     
28443     boxes : 0,
28444     inputName : '',
28445     thumbSize : 300,
28446     multiple : true,
28447     files : false,
28448     method : 'POST',
28449     url : '',
28450     paramName : 'imageUpload',
28451     toolTipName : 'filename',
28452     fieldLabel : '',
28453     labelWidth : 4,
28454     labelAlign : 'left',
28455     editable : true,
28456     delegates : false,
28457     xhr : false, 
28458     
28459     labellg : 0,
28460     labelmd : 0,
28461     labelsm : 0,
28462     labelxs : 0,
28463     
28464     getAutoCreate : function()
28465     {   
28466         var managerWidget = {
28467             tag : 'div',
28468             cls : 'roo-document-manager',
28469             cn : [
28470                 {
28471                     tag : 'input',
28472                     cls : 'roo-document-manager-selector',
28473                     type : 'file'
28474                 },
28475                 {
28476                     tag : 'div',
28477                     cls : 'roo-document-manager-uploader',
28478                     cn : [
28479                         {
28480                             tag : 'div',
28481                             cls : 'roo-document-manager-upload-btn',
28482                             html : '<i class="fa fa-plus"></i>'
28483                         }
28484                     ]
28485                     
28486                 }
28487             ]
28488         };
28489         
28490         var content = [
28491             {
28492                 tag : 'div',
28493                 cls : 'column col-md-12',
28494                 cn : managerWidget
28495             }
28496         ];
28497         
28498         if(this.fieldLabel.length){
28499             
28500             content = [
28501                 {
28502                     tag : 'div',
28503                     cls : 'column col-md-12',
28504                     html : this.fieldLabel
28505                 },
28506                 {
28507                     tag : 'div',
28508                     cls : 'column col-md-12',
28509                     cn : managerWidget
28510                 }
28511             ];
28512
28513             if(this.labelAlign == 'left'){
28514                 content = [
28515                     {
28516                         tag : 'div',
28517                         cls : 'column',
28518                         html : this.fieldLabel
28519                     },
28520                     {
28521                         tag : 'div',
28522                         cls : 'column',
28523                         cn : managerWidget
28524                     }
28525                 ];
28526                 
28527                 if(this.labelWidth > 12){
28528                     content[0].style = "width: " + this.labelWidth + 'px';
28529                 }
28530
28531                 if(this.labelWidth < 13 && this.labelmd == 0){
28532                     this.labelmd = this.labelWidth;
28533                 }
28534
28535                 if(this.labellg > 0){
28536                     content[0].cls += ' col-lg-' + this.labellg;
28537                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28538                 }
28539
28540                 if(this.labelmd > 0){
28541                     content[0].cls += ' col-md-' + this.labelmd;
28542                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28543                 }
28544
28545                 if(this.labelsm > 0){
28546                     content[0].cls += ' col-sm-' + this.labelsm;
28547                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28548                 }
28549
28550                 if(this.labelxs > 0){
28551                     content[0].cls += ' col-xs-' + this.labelxs;
28552                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28553                 }
28554                 
28555             }
28556         }
28557         
28558         var cfg = {
28559             tag : 'div',
28560             cls : 'row clearfix',
28561             cn : content
28562         };
28563         
28564         return cfg;
28565         
28566     },
28567     
28568     initEvents : function()
28569     {
28570         this.managerEl = this.el.select('.roo-document-manager', true).first();
28571         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28572         
28573         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28574         this.selectorEl.hide();
28575         
28576         if(this.multiple){
28577             this.selectorEl.attr('multiple', 'multiple');
28578         }
28579         
28580         this.selectorEl.on('change', this.onFileSelected, this);
28581         
28582         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28583         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28584         
28585         this.uploader.on('click', this.onUploaderClick, this);
28586         
28587         this.renderProgressDialog();
28588         
28589         var _this = this;
28590         
28591         window.addEventListener("resize", function() { _this.refresh(); } );
28592         
28593         this.fireEvent('initial', this);
28594     },
28595     
28596     renderProgressDialog : function()
28597     {
28598         var _this = this;
28599         
28600         this.progressDialog = new Roo.bootstrap.Modal({
28601             cls : 'roo-document-manager-progress-dialog',
28602             allow_close : false,
28603             title : '',
28604             buttons : [
28605                 {
28606                     name  :'cancel',
28607                     weight : 'danger',
28608                     html : 'Cancel'
28609                 }
28610             ], 
28611             listeners : { 
28612                 btnclick : function() {
28613                     _this.uploadCancel();
28614                     this.hide();
28615                 }
28616             }
28617         });
28618          
28619         this.progressDialog.render(Roo.get(document.body));
28620          
28621         this.progress = new Roo.bootstrap.Progress({
28622             cls : 'roo-document-manager-progress',
28623             active : true,
28624             striped : true
28625         });
28626         
28627         this.progress.render(this.progressDialog.getChildContainer());
28628         
28629         this.progressBar = new Roo.bootstrap.ProgressBar({
28630             cls : 'roo-document-manager-progress-bar',
28631             aria_valuenow : 0,
28632             aria_valuemin : 0,
28633             aria_valuemax : 12,
28634             panel : 'success'
28635         });
28636         
28637         this.progressBar.render(this.progress.getChildContainer());
28638     },
28639     
28640     onUploaderClick : function(e)
28641     {
28642         e.preventDefault();
28643      
28644         if(this.fireEvent('beforeselectfile', this) != false){
28645             this.selectorEl.dom.click();
28646         }
28647         
28648     },
28649     
28650     onFileSelected : function(e)
28651     {
28652         e.preventDefault();
28653         
28654         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28655             return;
28656         }
28657         
28658         Roo.each(this.selectorEl.dom.files, function(file){
28659             if(this.fireEvent('inspect', this, file) != false){
28660                 this.files.push(file);
28661             }
28662         }, this);
28663         
28664         this.queue();
28665         
28666     },
28667     
28668     queue : function()
28669     {
28670         this.selectorEl.dom.value = '';
28671         
28672         if(!this.files.length){
28673             return;
28674         }
28675         
28676         if(this.boxes > 0 && this.files.length > this.boxes){
28677             this.files = this.files.slice(0, this.boxes);
28678         }
28679         
28680         this.uploader.show();
28681         
28682         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28683             this.uploader.hide();
28684         }
28685         
28686         var _this = this;
28687         
28688         var files = [];
28689         
28690         var docs = [];
28691         
28692         Roo.each(this.files, function(file){
28693             
28694             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28695                 var f = this.renderPreview(file);
28696                 files.push(f);
28697                 return;
28698             }
28699             
28700             if(file.type.indexOf('image') != -1){
28701                 this.delegates.push(
28702                     (function(){
28703                         _this.process(file);
28704                     }).createDelegate(this)
28705                 );
28706         
28707                 return;
28708             }
28709             
28710             docs.push(
28711                 (function(){
28712                     _this.process(file);
28713                 }).createDelegate(this)
28714             );
28715             
28716         }, this);
28717         
28718         this.files = files;
28719         
28720         this.delegates = this.delegates.concat(docs);
28721         
28722         if(!this.delegates.length){
28723             this.refresh();
28724             return;
28725         }
28726         
28727         this.progressBar.aria_valuemax = this.delegates.length;
28728         
28729         this.arrange();
28730         
28731         return;
28732     },
28733     
28734     arrange : function()
28735     {
28736         if(!this.delegates.length){
28737             this.progressDialog.hide();
28738             this.refresh();
28739             return;
28740         }
28741         
28742         var delegate = this.delegates.shift();
28743         
28744         this.progressDialog.show();
28745         
28746         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28747         
28748         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28749         
28750         delegate();
28751     },
28752     
28753     refresh : function()
28754     {
28755         this.uploader.show();
28756         
28757         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28758             this.uploader.hide();
28759         }
28760         
28761         Roo.isTouch ? this.closable(false) : this.closable(true);
28762         
28763         this.fireEvent('refresh', this);
28764     },
28765     
28766     onRemove : function(e, el, o)
28767     {
28768         e.preventDefault();
28769         
28770         this.fireEvent('remove', this, o);
28771         
28772     },
28773     
28774     remove : function(o)
28775     {
28776         var files = [];
28777         
28778         Roo.each(this.files, function(file){
28779             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28780                 files.push(file);
28781                 return;
28782             }
28783
28784             o.target.remove();
28785
28786         }, this);
28787         
28788         this.files = files;
28789         
28790         this.refresh();
28791     },
28792     
28793     clear : function()
28794     {
28795         Roo.each(this.files, function(file){
28796             if(!file.target){
28797                 return;
28798             }
28799             
28800             file.target.remove();
28801
28802         }, this);
28803         
28804         this.files = [];
28805         
28806         this.refresh();
28807     },
28808     
28809     onClick : function(e, el, o)
28810     {
28811         e.preventDefault();
28812         
28813         this.fireEvent('click', this, o);
28814         
28815     },
28816     
28817     closable : function(closable)
28818     {
28819         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28820             
28821             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28822             
28823             if(closable){
28824                 el.show();
28825                 return;
28826             }
28827             
28828             el.hide();
28829             
28830         }, this);
28831     },
28832     
28833     xhrOnLoad : function(xhr)
28834     {
28835         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28836             el.remove();
28837         }, this);
28838         
28839         if (xhr.readyState !== 4) {
28840             this.arrange();
28841             this.fireEvent('exception', this, xhr);
28842             return;
28843         }
28844
28845         var response = Roo.decode(xhr.responseText);
28846         
28847         if(!response.success){
28848             this.arrange();
28849             this.fireEvent('exception', this, xhr);
28850             return;
28851         }
28852         
28853         var file = this.renderPreview(response.data);
28854         
28855         this.files.push(file);
28856         
28857         this.arrange();
28858         
28859         this.fireEvent('afterupload', this, xhr);
28860         
28861     },
28862     
28863     xhrOnError : function(xhr)
28864     {
28865         Roo.log('xhr on error');
28866         
28867         var response = Roo.decode(xhr.responseText);
28868           
28869         Roo.log(response);
28870         
28871         this.arrange();
28872     },
28873     
28874     process : function(file)
28875     {
28876         if(this.fireEvent('process', this, file) !== false){
28877             if(this.editable && file.type.indexOf('image') != -1){
28878                 this.fireEvent('edit', this, file);
28879                 return;
28880             }
28881
28882             this.uploadStart(file, false);
28883
28884             return;
28885         }
28886         
28887     },
28888     
28889     uploadStart : function(file, crop)
28890     {
28891         this.xhr = new XMLHttpRequest();
28892         
28893         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28894             this.arrange();
28895             return;
28896         }
28897         
28898         file.xhr = this.xhr;
28899             
28900         this.managerEl.createChild({
28901             tag : 'div',
28902             cls : 'roo-document-manager-loading',
28903             cn : [
28904                 {
28905                     tag : 'div',
28906                     tooltip : file.name,
28907                     cls : 'roo-document-manager-thumb',
28908                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28909                 }
28910             ]
28911
28912         });
28913
28914         this.xhr.open(this.method, this.url, true);
28915         
28916         var headers = {
28917             "Accept": "application/json",
28918             "Cache-Control": "no-cache",
28919             "X-Requested-With": "XMLHttpRequest"
28920         };
28921         
28922         for (var headerName in headers) {
28923             var headerValue = headers[headerName];
28924             if (headerValue) {
28925                 this.xhr.setRequestHeader(headerName, headerValue);
28926             }
28927         }
28928         
28929         var _this = this;
28930         
28931         this.xhr.onload = function()
28932         {
28933             _this.xhrOnLoad(_this.xhr);
28934         }
28935         
28936         this.xhr.onerror = function()
28937         {
28938             _this.xhrOnError(_this.xhr);
28939         }
28940         
28941         var formData = new FormData();
28942
28943         formData.append('returnHTML', 'NO');
28944         
28945         if(crop){
28946             formData.append('crop', crop);
28947         }
28948         
28949         formData.append(this.paramName, file, file.name);
28950         
28951         var options = {
28952             file : file, 
28953             manually : false
28954         };
28955         
28956         if(this.fireEvent('prepare', this, formData, options) != false){
28957             
28958             if(options.manually){
28959                 return;
28960             }
28961             
28962             this.xhr.send(formData);
28963             return;
28964         };
28965         
28966         this.uploadCancel();
28967     },
28968     
28969     uploadCancel : function()
28970     {
28971         if (this.xhr) {
28972             this.xhr.abort();
28973         }
28974         
28975         this.delegates = [];
28976         
28977         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28978             el.remove();
28979         }, this);
28980         
28981         this.arrange();
28982     },
28983     
28984     renderPreview : function(file)
28985     {
28986         if(typeof(file.target) != 'undefined' && file.target){
28987             return file;
28988         }
28989         
28990         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28991         
28992         var previewEl = this.managerEl.createChild({
28993             tag : 'div',
28994             cls : 'roo-document-manager-preview',
28995             cn : [
28996                 {
28997                     tag : 'div',
28998                     tooltip : file[this.toolTipName],
28999                     cls : 'roo-document-manager-thumb',
29000                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29001                 },
29002                 {
29003                     tag : 'button',
29004                     cls : 'close',
29005                     html : '<i class="fa fa-times-circle"></i>'
29006                 }
29007             ]
29008         });
29009
29010         var close = previewEl.select('button.close', true).first();
29011
29012         close.on('click', this.onRemove, this, file);
29013
29014         file.target = previewEl;
29015
29016         var image = previewEl.select('img', true).first();
29017         
29018         var _this = this;
29019         
29020         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29021         
29022         image.on('click', this.onClick, this, file);
29023         
29024         return file;
29025         
29026     },
29027     
29028     onPreviewLoad : function(file, image)
29029     {
29030         if(typeof(file.target) == 'undefined' || !file.target){
29031             return;
29032         }
29033         
29034         var width = image.dom.naturalWidth || image.dom.width;
29035         var height = image.dom.naturalHeight || image.dom.height;
29036         
29037         if(width > height){
29038             file.target.addClass('wide');
29039             return;
29040         }
29041         
29042         file.target.addClass('tall');
29043         return;
29044         
29045     },
29046     
29047     uploadFromSource : function(file, crop)
29048     {
29049         this.xhr = new XMLHttpRequest();
29050         
29051         this.managerEl.createChild({
29052             tag : 'div',
29053             cls : 'roo-document-manager-loading',
29054             cn : [
29055                 {
29056                     tag : 'div',
29057                     tooltip : file.name,
29058                     cls : 'roo-document-manager-thumb',
29059                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29060                 }
29061             ]
29062
29063         });
29064
29065         this.xhr.open(this.method, this.url, true);
29066         
29067         var headers = {
29068             "Accept": "application/json",
29069             "Cache-Control": "no-cache",
29070             "X-Requested-With": "XMLHttpRequest"
29071         };
29072         
29073         for (var headerName in headers) {
29074             var headerValue = headers[headerName];
29075             if (headerValue) {
29076                 this.xhr.setRequestHeader(headerName, headerValue);
29077             }
29078         }
29079         
29080         var _this = this;
29081         
29082         this.xhr.onload = function()
29083         {
29084             _this.xhrOnLoad(_this.xhr);
29085         }
29086         
29087         this.xhr.onerror = function()
29088         {
29089             _this.xhrOnError(_this.xhr);
29090         }
29091         
29092         var formData = new FormData();
29093
29094         formData.append('returnHTML', 'NO');
29095         
29096         formData.append('crop', crop);
29097         
29098         if(typeof(file.filename) != 'undefined'){
29099             formData.append('filename', file.filename);
29100         }
29101         
29102         if(typeof(file.mimetype) != 'undefined'){
29103             formData.append('mimetype', file.mimetype);
29104         }
29105         
29106         Roo.log(formData);
29107         
29108         if(this.fireEvent('prepare', this, formData) != false){
29109             this.xhr.send(formData);
29110         };
29111     }
29112 });
29113
29114 /*
29115 * Licence: LGPL
29116 */
29117
29118 /**
29119  * @class Roo.bootstrap.DocumentViewer
29120  * @extends Roo.bootstrap.Component
29121  * Bootstrap DocumentViewer class
29122  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29123  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29124  * 
29125  * @constructor
29126  * Create a new DocumentViewer
29127  * @param {Object} config The config object
29128  */
29129
29130 Roo.bootstrap.DocumentViewer = function(config){
29131     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29132     
29133     this.addEvents({
29134         /**
29135          * @event initial
29136          * Fire after initEvent
29137          * @param {Roo.bootstrap.DocumentViewer} this
29138          */
29139         "initial" : true,
29140         /**
29141          * @event click
29142          * Fire after click
29143          * @param {Roo.bootstrap.DocumentViewer} this
29144          */
29145         "click" : true,
29146         /**
29147          * @event download
29148          * Fire after download button
29149          * @param {Roo.bootstrap.DocumentViewer} this
29150          */
29151         "download" : true,
29152         /**
29153          * @event trash
29154          * Fire after trash button
29155          * @param {Roo.bootstrap.DocumentViewer} this
29156          */
29157         "trash" : true
29158         
29159     });
29160 };
29161
29162 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29163     
29164     showDownload : true,
29165     
29166     showTrash : true,
29167     
29168     getAutoCreate : function()
29169     {
29170         var cfg = {
29171             tag : 'div',
29172             cls : 'roo-document-viewer',
29173             cn : [
29174                 {
29175                     tag : 'div',
29176                     cls : 'roo-document-viewer-body',
29177                     cn : [
29178                         {
29179                             tag : 'div',
29180                             cls : 'roo-document-viewer-thumb',
29181                             cn : [
29182                                 {
29183                                     tag : 'img',
29184                                     cls : 'roo-document-viewer-image'
29185                                 }
29186                             ]
29187                         }
29188                     ]
29189                 },
29190                 {
29191                     tag : 'div',
29192                     cls : 'roo-document-viewer-footer',
29193                     cn : {
29194                         tag : 'div',
29195                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29196                         cn : [
29197                             {
29198                                 tag : 'div',
29199                                 cls : 'btn-group roo-document-viewer-download',
29200                                 cn : [
29201                                     {
29202                                         tag : 'button',
29203                                         cls : 'btn btn-default',
29204                                         html : '<i class="fa fa-download"></i>'
29205                                     }
29206                                 ]
29207                             },
29208                             {
29209                                 tag : 'div',
29210                                 cls : 'btn-group roo-document-viewer-trash',
29211                                 cn : [
29212                                     {
29213                                         tag : 'button',
29214                                         cls : 'btn btn-default',
29215                                         html : '<i class="fa fa-trash"></i>'
29216                                     }
29217                                 ]
29218                             }
29219                         ]
29220                     }
29221                 }
29222             ]
29223         };
29224         
29225         return cfg;
29226     },
29227     
29228     initEvents : function()
29229     {
29230         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29231         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29232         
29233         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29234         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29235         
29236         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29237         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29238         
29239         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29240         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29241         
29242         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29243         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29244         
29245         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29246         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29247         
29248         this.bodyEl.on('click', this.onClick, this);
29249         this.downloadBtn.on('click', this.onDownload, this);
29250         this.trashBtn.on('click', this.onTrash, this);
29251         
29252         this.downloadBtn.hide();
29253         this.trashBtn.hide();
29254         
29255         if(this.showDownload){
29256             this.downloadBtn.show();
29257         }
29258         
29259         if(this.showTrash){
29260             this.trashBtn.show();
29261         }
29262         
29263         if(!this.showDownload && !this.showTrash) {
29264             this.footerEl.hide();
29265         }
29266         
29267     },
29268     
29269     initial : function()
29270     {
29271         this.fireEvent('initial', this);
29272         
29273     },
29274     
29275     onClick : function(e)
29276     {
29277         e.preventDefault();
29278         
29279         this.fireEvent('click', this);
29280     },
29281     
29282     onDownload : function(e)
29283     {
29284         e.preventDefault();
29285         
29286         this.fireEvent('download', this);
29287     },
29288     
29289     onTrash : function(e)
29290     {
29291         e.preventDefault();
29292         
29293         this.fireEvent('trash', this);
29294     }
29295     
29296 });
29297 /*
29298  * - LGPL
29299  *
29300  * nav progress bar
29301  * 
29302  */
29303
29304 /**
29305  * @class Roo.bootstrap.NavProgressBar
29306  * @extends Roo.bootstrap.Component
29307  * Bootstrap NavProgressBar class
29308  * 
29309  * @constructor
29310  * Create a new nav progress bar
29311  * @param {Object} config The config object
29312  */
29313
29314 Roo.bootstrap.NavProgressBar = function(config){
29315     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29316
29317     this.bullets = this.bullets || [];
29318    
29319 //    Roo.bootstrap.NavProgressBar.register(this);
29320      this.addEvents({
29321         /**
29322              * @event changed
29323              * Fires when the active item changes
29324              * @param {Roo.bootstrap.NavProgressBar} this
29325              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29326              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29327          */
29328         'changed': true
29329      });
29330     
29331 };
29332
29333 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29334     
29335     bullets : [],
29336     barItems : [],
29337     
29338     getAutoCreate : function()
29339     {
29340         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29341         
29342         cfg = {
29343             tag : 'div',
29344             cls : 'roo-navigation-bar-group',
29345             cn : [
29346                 {
29347                     tag : 'div',
29348                     cls : 'roo-navigation-top-bar'
29349                 },
29350                 {
29351                     tag : 'div',
29352                     cls : 'roo-navigation-bullets-bar',
29353                     cn : [
29354                         {
29355                             tag : 'ul',
29356                             cls : 'roo-navigation-bar'
29357                         }
29358                     ]
29359                 },
29360                 
29361                 {
29362                     tag : 'div',
29363                     cls : 'roo-navigation-bottom-bar'
29364                 }
29365             ]
29366             
29367         };
29368         
29369         return cfg;
29370         
29371     },
29372     
29373     initEvents: function() 
29374     {
29375         
29376     },
29377     
29378     onRender : function(ct, position) 
29379     {
29380         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29381         
29382         if(this.bullets.length){
29383             Roo.each(this.bullets, function(b){
29384                this.addItem(b);
29385             }, this);
29386         }
29387         
29388         this.format();
29389         
29390     },
29391     
29392     addItem : function(cfg)
29393     {
29394         var item = new Roo.bootstrap.NavProgressItem(cfg);
29395         
29396         item.parentId = this.id;
29397         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29398         
29399         if(cfg.html){
29400             var top = new Roo.bootstrap.Element({
29401                 tag : 'div',
29402                 cls : 'roo-navigation-bar-text'
29403             });
29404             
29405             var bottom = new Roo.bootstrap.Element({
29406                 tag : 'div',
29407                 cls : 'roo-navigation-bar-text'
29408             });
29409             
29410             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29411             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29412             
29413             var topText = new Roo.bootstrap.Element({
29414                 tag : 'span',
29415                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29416             });
29417             
29418             var bottomText = new Roo.bootstrap.Element({
29419                 tag : 'span',
29420                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29421             });
29422             
29423             topText.onRender(top.el, null);
29424             bottomText.onRender(bottom.el, null);
29425             
29426             item.topEl = top;
29427             item.bottomEl = bottom;
29428         }
29429         
29430         this.barItems.push(item);
29431         
29432         return item;
29433     },
29434     
29435     getActive : function()
29436     {
29437         var active = false;
29438         
29439         Roo.each(this.barItems, function(v){
29440             
29441             if (!v.isActive()) {
29442                 return;
29443             }
29444             
29445             active = v;
29446             return false;
29447             
29448         });
29449         
29450         return active;
29451     },
29452     
29453     setActiveItem : function(item)
29454     {
29455         var prev = false;
29456         
29457         Roo.each(this.barItems, function(v){
29458             if (v.rid == item.rid) {
29459                 return ;
29460             }
29461             
29462             if (v.isActive()) {
29463                 v.setActive(false);
29464                 prev = v;
29465             }
29466         });
29467
29468         item.setActive(true);
29469         
29470         this.fireEvent('changed', this, item, prev);
29471     },
29472     
29473     getBarItem: function(rid)
29474     {
29475         var ret = false;
29476         
29477         Roo.each(this.barItems, function(e) {
29478             if (e.rid != rid) {
29479                 return;
29480             }
29481             
29482             ret =  e;
29483             return false;
29484         });
29485         
29486         return ret;
29487     },
29488     
29489     indexOfItem : function(item)
29490     {
29491         var index = false;
29492         
29493         Roo.each(this.barItems, function(v, i){
29494             
29495             if (v.rid != item.rid) {
29496                 return;
29497             }
29498             
29499             index = i;
29500             return false
29501         });
29502         
29503         return index;
29504     },
29505     
29506     setActiveNext : function()
29507     {
29508         var i = this.indexOfItem(this.getActive());
29509         
29510         if (i > this.barItems.length) {
29511             return;
29512         }
29513         
29514         this.setActiveItem(this.barItems[i+1]);
29515     },
29516     
29517     setActivePrev : function()
29518     {
29519         var i = this.indexOfItem(this.getActive());
29520         
29521         if (i  < 1) {
29522             return;
29523         }
29524         
29525         this.setActiveItem(this.barItems[i-1]);
29526     },
29527     
29528     format : function()
29529     {
29530         if(!this.barItems.length){
29531             return;
29532         }
29533      
29534         var width = 100 / this.barItems.length;
29535         
29536         Roo.each(this.barItems, function(i){
29537             i.el.setStyle('width', width + '%');
29538             i.topEl.el.setStyle('width', width + '%');
29539             i.bottomEl.el.setStyle('width', width + '%');
29540         }, this);
29541         
29542     }
29543     
29544 });
29545 /*
29546  * - LGPL
29547  *
29548  * Nav Progress Item
29549  * 
29550  */
29551
29552 /**
29553  * @class Roo.bootstrap.NavProgressItem
29554  * @extends Roo.bootstrap.Component
29555  * Bootstrap NavProgressItem class
29556  * @cfg {String} rid the reference id
29557  * @cfg {Boolean} active (true|false) Is item active default false
29558  * @cfg {Boolean} disabled (true|false) Is item active default false
29559  * @cfg {String} html
29560  * @cfg {String} position (top|bottom) text position default bottom
29561  * @cfg {String} icon show icon instead of number
29562  * 
29563  * @constructor
29564  * Create a new NavProgressItem
29565  * @param {Object} config The config object
29566  */
29567 Roo.bootstrap.NavProgressItem = function(config){
29568     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29569     this.addEvents({
29570         // raw events
29571         /**
29572          * @event click
29573          * The raw click event for the entire grid.
29574          * @param {Roo.bootstrap.NavProgressItem} this
29575          * @param {Roo.EventObject} e
29576          */
29577         "click" : true
29578     });
29579    
29580 };
29581
29582 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29583     
29584     rid : '',
29585     active : false,
29586     disabled : false,
29587     html : '',
29588     position : 'bottom',
29589     icon : false,
29590     
29591     getAutoCreate : function()
29592     {
29593         var iconCls = 'roo-navigation-bar-item-icon';
29594         
29595         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29596         
29597         var cfg = {
29598             tag: 'li',
29599             cls: 'roo-navigation-bar-item',
29600             cn : [
29601                 {
29602                     tag : 'i',
29603                     cls : iconCls
29604                 }
29605             ]
29606         };
29607         
29608         if(this.active){
29609             cfg.cls += ' active';
29610         }
29611         if(this.disabled){
29612             cfg.cls += ' disabled';
29613         }
29614         
29615         return cfg;
29616     },
29617     
29618     disable : function()
29619     {
29620         this.setDisabled(true);
29621     },
29622     
29623     enable : function()
29624     {
29625         this.setDisabled(false);
29626     },
29627     
29628     initEvents: function() 
29629     {
29630         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29631         
29632         this.iconEl.on('click', this.onClick, this);
29633     },
29634     
29635     onClick : function(e)
29636     {
29637         e.preventDefault();
29638         
29639         if(this.disabled){
29640             return;
29641         }
29642         
29643         if(this.fireEvent('click', this, e) === false){
29644             return;
29645         };
29646         
29647         this.parent().setActiveItem(this);
29648     },
29649     
29650     isActive: function () 
29651     {
29652         return this.active;
29653     },
29654     
29655     setActive : function(state)
29656     {
29657         if(this.active == state){
29658             return;
29659         }
29660         
29661         this.active = state;
29662         
29663         if (state) {
29664             this.el.addClass('active');
29665             return;
29666         }
29667         
29668         this.el.removeClass('active');
29669         
29670         return;
29671     },
29672     
29673     setDisabled : function(state)
29674     {
29675         if(this.disabled == state){
29676             return;
29677         }
29678         
29679         this.disabled = state;
29680         
29681         if (state) {
29682             this.el.addClass('disabled');
29683             return;
29684         }
29685         
29686         this.el.removeClass('disabled');
29687     },
29688     
29689     tooltipEl : function()
29690     {
29691         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29692     }
29693 });
29694  
29695
29696  /*
29697  * - LGPL
29698  *
29699  * FieldLabel
29700  * 
29701  */
29702
29703 /**
29704  * @class Roo.bootstrap.FieldLabel
29705  * @extends Roo.bootstrap.Component
29706  * Bootstrap FieldLabel class
29707  * @cfg {String} html contents of the element
29708  * @cfg {String} tag tag of the element default label
29709  * @cfg {String} cls class of the element
29710  * @cfg {String} target label target 
29711  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29712  * @cfg {String} invalidClass default "text-warning"
29713  * @cfg {String} validClass default "text-success"
29714  * @cfg {String} iconTooltip default "This field is required"
29715  * @cfg {String} indicatorpos (left|right) default left
29716  * 
29717  * @constructor
29718  * Create a new FieldLabel
29719  * @param {Object} config The config object
29720  */
29721
29722 Roo.bootstrap.FieldLabel = function(config){
29723     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29724     
29725     this.addEvents({
29726             /**
29727              * @event invalid
29728              * Fires after the field has been marked as invalid.
29729              * @param {Roo.form.FieldLabel} this
29730              * @param {String} msg The validation message
29731              */
29732             invalid : true,
29733             /**
29734              * @event valid
29735              * Fires after the field has been validated with no errors.
29736              * @param {Roo.form.FieldLabel} this
29737              */
29738             valid : true
29739         });
29740 };
29741
29742 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29743     
29744     tag: 'label',
29745     cls: '',
29746     html: '',
29747     target: '',
29748     allowBlank : true,
29749     invalidClass : 'has-warning',
29750     validClass : 'has-success',
29751     iconTooltip : 'This field is required',
29752     indicatorpos : 'left',
29753     
29754     getAutoCreate : function(){
29755         
29756         var cfg = {
29757             tag : this.tag,
29758             cls : 'roo-bootstrap-field-label ' + this.cls,
29759             for : this.target,
29760             cn : [
29761                 {
29762                     tag : 'i',
29763                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29764                     tooltip : this.iconTooltip
29765                 },
29766                 {
29767                     tag : 'span',
29768                     html : this.html
29769                 }
29770             ] 
29771         };
29772         
29773         if(this.indicatorpos == 'right'){
29774             var cfg = {
29775                 tag : this.tag,
29776                 cls : 'roo-bootstrap-field-label ' + this.cls,
29777                 for : this.target,
29778                 cn : [
29779                     {
29780                         tag : 'span',
29781                         html : this.html
29782                     },
29783                     {
29784                         tag : 'i',
29785                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29786                         tooltip : this.iconTooltip
29787                     }
29788                 ] 
29789             };
29790         }
29791         
29792         return cfg;
29793     },
29794     
29795     initEvents: function() 
29796     {
29797         Roo.bootstrap.Element.superclass.initEvents.call(this);
29798         
29799         this.indicator = this.indicatorEl();
29800         
29801         if(this.indicator){
29802             this.indicator.removeClass('visible');
29803             this.indicator.addClass('invisible');
29804         }
29805         
29806         Roo.bootstrap.FieldLabel.register(this);
29807     },
29808     
29809     indicatorEl : function()
29810     {
29811         var indicator = this.el.select('i.roo-required-indicator',true).first();
29812         
29813         if(!indicator){
29814             return false;
29815         }
29816         
29817         return indicator;
29818         
29819     },
29820     
29821     /**
29822      * Mark this field as valid
29823      */
29824     markValid : function()
29825     {
29826         if(this.indicator){
29827             this.indicator.removeClass('visible');
29828             this.indicator.addClass('invisible');
29829         }
29830         
29831         this.el.removeClass(this.invalidClass);
29832         
29833         this.el.addClass(this.validClass);
29834         
29835         this.fireEvent('valid', this);
29836     },
29837     
29838     /**
29839      * Mark this field as invalid
29840      * @param {String} msg The validation message
29841      */
29842     markInvalid : function(msg)
29843     {
29844         if(this.indicator){
29845             this.indicator.removeClass('invisible');
29846             this.indicator.addClass('visible');
29847         }
29848         
29849         this.el.removeClass(this.validClass);
29850         
29851         this.el.addClass(this.invalidClass);
29852         
29853         this.fireEvent('invalid', this, msg);
29854     }
29855     
29856    
29857 });
29858
29859 Roo.apply(Roo.bootstrap.FieldLabel, {
29860     
29861     groups: {},
29862     
29863      /**
29864     * register a FieldLabel Group
29865     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29866     */
29867     register : function(label)
29868     {
29869         if(this.groups.hasOwnProperty(label.target)){
29870             return;
29871         }
29872      
29873         this.groups[label.target] = label;
29874         
29875     },
29876     /**
29877     * fetch a FieldLabel Group based on the target
29878     * @param {string} target
29879     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29880     */
29881     get: function(target) {
29882         if (typeof(this.groups[target]) == 'undefined') {
29883             return false;
29884         }
29885         
29886         return this.groups[target] ;
29887     }
29888 });
29889
29890  
29891
29892  /*
29893  * - LGPL
29894  *
29895  * page DateSplitField.
29896  * 
29897  */
29898
29899
29900 /**
29901  * @class Roo.bootstrap.DateSplitField
29902  * @extends Roo.bootstrap.Component
29903  * Bootstrap DateSplitField class
29904  * @cfg {string} fieldLabel - the label associated
29905  * @cfg {Number} labelWidth set the width of label (0-12)
29906  * @cfg {String} labelAlign (top|left)
29907  * @cfg {Boolean} dayAllowBlank (true|false) default false
29908  * @cfg {Boolean} monthAllowBlank (true|false) default false
29909  * @cfg {Boolean} yearAllowBlank (true|false) default false
29910  * @cfg {string} dayPlaceholder 
29911  * @cfg {string} monthPlaceholder
29912  * @cfg {string} yearPlaceholder
29913  * @cfg {string} dayFormat default 'd'
29914  * @cfg {string} monthFormat default 'm'
29915  * @cfg {string} yearFormat default 'Y'
29916  * @cfg {Number} labellg set the width of label (1-12)
29917  * @cfg {Number} labelmd set the width of label (1-12)
29918  * @cfg {Number} labelsm set the width of label (1-12)
29919  * @cfg {Number} labelxs set the width of label (1-12)
29920
29921  *     
29922  * @constructor
29923  * Create a new DateSplitField
29924  * @param {Object} config The config object
29925  */
29926
29927 Roo.bootstrap.DateSplitField = function(config){
29928     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29929     
29930     this.addEvents({
29931         // raw events
29932          /**
29933          * @event years
29934          * getting the data of years
29935          * @param {Roo.bootstrap.DateSplitField} this
29936          * @param {Object} years
29937          */
29938         "years" : true,
29939         /**
29940          * @event days
29941          * getting the data of days
29942          * @param {Roo.bootstrap.DateSplitField} this
29943          * @param {Object} days
29944          */
29945         "days" : true,
29946         /**
29947          * @event invalid
29948          * Fires after the field has been marked as invalid.
29949          * @param {Roo.form.Field} this
29950          * @param {String} msg The validation message
29951          */
29952         invalid : true,
29953        /**
29954          * @event valid
29955          * Fires after the field has been validated with no errors.
29956          * @param {Roo.form.Field} this
29957          */
29958         valid : true
29959     });
29960 };
29961
29962 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29963     
29964     fieldLabel : '',
29965     labelAlign : 'top',
29966     labelWidth : 3,
29967     dayAllowBlank : false,
29968     monthAllowBlank : false,
29969     yearAllowBlank : false,
29970     dayPlaceholder : '',
29971     monthPlaceholder : '',
29972     yearPlaceholder : '',
29973     dayFormat : 'd',
29974     monthFormat : 'm',
29975     yearFormat : 'Y',
29976     isFormField : true,
29977     labellg : 0,
29978     labelmd : 0,
29979     labelsm : 0,
29980     labelxs : 0,
29981     
29982     getAutoCreate : function()
29983     {
29984         var cfg = {
29985             tag : 'div',
29986             cls : 'row roo-date-split-field-group',
29987             cn : [
29988                 {
29989                     tag : 'input',
29990                     type : 'hidden',
29991                     cls : 'form-hidden-field roo-date-split-field-group-value',
29992                     name : this.name
29993                 }
29994             ]
29995         };
29996         
29997         var labelCls = 'col-md-12';
29998         var contentCls = 'col-md-4';
29999         
30000         if(this.fieldLabel){
30001             
30002             var label = {
30003                 tag : 'div',
30004                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30005                 cn : [
30006                     {
30007                         tag : 'label',
30008                         html : this.fieldLabel
30009                     }
30010                 ]
30011             };
30012             
30013             if(this.labelAlign == 'left'){
30014             
30015                 if(this.labelWidth > 12){
30016                     label.style = "width: " + this.labelWidth + 'px';
30017                 }
30018
30019                 if(this.labelWidth < 13 && this.labelmd == 0){
30020                     this.labelmd = this.labelWidth;
30021                 }
30022
30023                 if(this.labellg > 0){
30024                     labelCls = ' col-lg-' + this.labellg;
30025                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30026                 }
30027
30028                 if(this.labelmd > 0){
30029                     labelCls = ' col-md-' + this.labelmd;
30030                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30031                 }
30032
30033                 if(this.labelsm > 0){
30034                     labelCls = ' col-sm-' + this.labelsm;
30035                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30036                 }
30037
30038                 if(this.labelxs > 0){
30039                     labelCls = ' col-xs-' + this.labelxs;
30040                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30041                 }
30042             }
30043             
30044             label.cls += ' ' + labelCls;
30045             
30046             cfg.cn.push(label);
30047         }
30048         
30049         Roo.each(['day', 'month', 'year'], function(t){
30050             cfg.cn.push({
30051                 tag : 'div',
30052                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30053             });
30054         }, this);
30055         
30056         return cfg;
30057     },
30058     
30059     inputEl: function ()
30060     {
30061         return this.el.select('.roo-date-split-field-group-value', true).first();
30062     },
30063     
30064     onRender : function(ct, position) 
30065     {
30066         var _this = this;
30067         
30068         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30069         
30070         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30071         
30072         this.dayField = new Roo.bootstrap.ComboBox({
30073             allowBlank : this.dayAllowBlank,
30074             alwaysQuery : true,
30075             displayField : 'value',
30076             editable : false,
30077             fieldLabel : '',
30078             forceSelection : true,
30079             mode : 'local',
30080             placeholder : this.dayPlaceholder,
30081             selectOnFocus : true,
30082             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30083             triggerAction : 'all',
30084             typeAhead : true,
30085             valueField : 'value',
30086             store : new Roo.data.SimpleStore({
30087                 data : (function() {    
30088                     var days = [];
30089                     _this.fireEvent('days', _this, days);
30090                     return days;
30091                 })(),
30092                 fields : [ 'value' ]
30093             }),
30094             listeners : {
30095                 select : function (_self, record, index)
30096                 {
30097                     _this.setValue(_this.getValue());
30098                 }
30099             }
30100         });
30101
30102         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30103         
30104         this.monthField = new Roo.bootstrap.MonthField({
30105             after : '<i class=\"fa fa-calendar\"></i>',
30106             allowBlank : this.monthAllowBlank,
30107             placeholder : this.monthPlaceholder,
30108             readOnly : true,
30109             listeners : {
30110                 render : function (_self)
30111                 {
30112                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30113                         e.preventDefault();
30114                         _self.focus();
30115                     });
30116                 },
30117                 select : function (_self, oldvalue, newvalue)
30118                 {
30119                     _this.setValue(_this.getValue());
30120                 }
30121             }
30122         });
30123         
30124         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30125         
30126         this.yearField = new Roo.bootstrap.ComboBox({
30127             allowBlank : this.yearAllowBlank,
30128             alwaysQuery : true,
30129             displayField : 'value',
30130             editable : false,
30131             fieldLabel : '',
30132             forceSelection : true,
30133             mode : 'local',
30134             placeholder : this.yearPlaceholder,
30135             selectOnFocus : true,
30136             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30137             triggerAction : 'all',
30138             typeAhead : true,
30139             valueField : 'value',
30140             store : new Roo.data.SimpleStore({
30141                 data : (function() {
30142                     var years = [];
30143                     _this.fireEvent('years', _this, years);
30144                     return years;
30145                 })(),
30146                 fields : [ 'value' ]
30147             }),
30148             listeners : {
30149                 select : function (_self, record, index)
30150                 {
30151                     _this.setValue(_this.getValue());
30152                 }
30153             }
30154         });
30155
30156         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30157     },
30158     
30159     setValue : function(v, format)
30160     {
30161         this.inputEl.dom.value = v;
30162         
30163         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30164         
30165         var d = Date.parseDate(v, f);
30166         
30167         if(!d){
30168             this.validate();
30169             return;
30170         }
30171         
30172         this.setDay(d.format(this.dayFormat));
30173         this.setMonth(d.format(this.monthFormat));
30174         this.setYear(d.format(this.yearFormat));
30175         
30176         this.validate();
30177         
30178         return;
30179     },
30180     
30181     setDay : function(v)
30182     {
30183         this.dayField.setValue(v);
30184         this.inputEl.dom.value = this.getValue();
30185         this.validate();
30186         return;
30187     },
30188     
30189     setMonth : function(v)
30190     {
30191         this.monthField.setValue(v, true);
30192         this.inputEl.dom.value = this.getValue();
30193         this.validate();
30194         return;
30195     },
30196     
30197     setYear : function(v)
30198     {
30199         this.yearField.setValue(v);
30200         this.inputEl.dom.value = this.getValue();
30201         this.validate();
30202         return;
30203     },
30204     
30205     getDay : function()
30206     {
30207         return this.dayField.getValue();
30208     },
30209     
30210     getMonth : function()
30211     {
30212         return this.monthField.getValue();
30213     },
30214     
30215     getYear : function()
30216     {
30217         return this.yearField.getValue();
30218     },
30219     
30220     getValue : function()
30221     {
30222         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30223         
30224         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30225         
30226         return date;
30227     },
30228     
30229     reset : function()
30230     {
30231         this.setDay('');
30232         this.setMonth('');
30233         this.setYear('');
30234         this.inputEl.dom.value = '';
30235         this.validate();
30236         return;
30237     },
30238     
30239     validate : function()
30240     {
30241         var d = this.dayField.validate();
30242         var m = this.monthField.validate();
30243         var y = this.yearField.validate();
30244         
30245         var valid = true;
30246         
30247         if(
30248                 (!this.dayAllowBlank && !d) ||
30249                 (!this.monthAllowBlank && !m) ||
30250                 (!this.yearAllowBlank && !y)
30251         ){
30252             valid = false;
30253         }
30254         
30255         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30256             return valid;
30257         }
30258         
30259         if(valid){
30260             this.markValid();
30261             return valid;
30262         }
30263         
30264         this.markInvalid();
30265         
30266         return valid;
30267     },
30268     
30269     markValid : function()
30270     {
30271         
30272         var label = this.el.select('label', true).first();
30273         var icon = this.el.select('i.fa-star', true).first();
30274
30275         if(label && icon){
30276             icon.remove();
30277         }
30278         
30279         this.fireEvent('valid', this);
30280     },
30281     
30282      /**
30283      * Mark this field as invalid
30284      * @param {String} msg The validation message
30285      */
30286     markInvalid : function(msg)
30287     {
30288         
30289         var label = this.el.select('label', true).first();
30290         var icon = this.el.select('i.fa-star', true).first();
30291
30292         if(label && !icon){
30293             this.el.select('.roo-date-split-field-label', true).createChild({
30294                 tag : 'i',
30295                 cls : 'text-danger fa fa-lg fa-star',
30296                 tooltip : 'This field is required',
30297                 style : 'margin-right:5px;'
30298             }, label, true);
30299         }
30300         
30301         this.fireEvent('invalid', this, msg);
30302     },
30303     
30304     clearInvalid : function()
30305     {
30306         var label = this.el.select('label', true).first();
30307         var icon = this.el.select('i.fa-star', true).first();
30308
30309         if(label && icon){
30310             icon.remove();
30311         }
30312         
30313         this.fireEvent('valid', this);
30314     },
30315     
30316     getName: function()
30317     {
30318         return this.name;
30319     }
30320     
30321 });
30322
30323  /**
30324  *
30325  * This is based on 
30326  * http://masonry.desandro.com
30327  *
30328  * The idea is to render all the bricks based on vertical width...
30329  *
30330  * The original code extends 'outlayer' - we might need to use that....
30331  * 
30332  */
30333
30334
30335 /**
30336  * @class Roo.bootstrap.LayoutMasonry
30337  * @extends Roo.bootstrap.Component
30338  * Bootstrap Layout Masonry class
30339  * 
30340  * @constructor
30341  * Create a new Element
30342  * @param {Object} config The config object
30343  */
30344
30345 Roo.bootstrap.LayoutMasonry = function(config){
30346     
30347     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30348     
30349     this.bricks = [];
30350     
30351     Roo.bootstrap.LayoutMasonry.register(this);
30352     
30353     this.addEvents({
30354         // raw events
30355         /**
30356          * @event layout
30357          * Fire after layout the items
30358          * @param {Roo.bootstrap.LayoutMasonry} this
30359          * @param {Roo.EventObject} e
30360          */
30361         "layout" : true
30362     });
30363     
30364 };
30365
30366 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30367     
30368     /**
30369      * @cfg {Boolean} isLayoutInstant = no animation?
30370      */   
30371     isLayoutInstant : false, // needed?
30372    
30373     /**
30374      * @cfg {Number} boxWidth  width of the columns
30375      */   
30376     boxWidth : 450,
30377     
30378       /**
30379      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30380      */   
30381     boxHeight : 0,
30382     
30383     /**
30384      * @cfg {Number} padWidth padding below box..
30385      */   
30386     padWidth : 10, 
30387     
30388     /**
30389      * @cfg {Number} gutter gutter width..
30390      */   
30391     gutter : 10,
30392     
30393      /**
30394      * @cfg {Number} maxCols maximum number of columns
30395      */   
30396     
30397     maxCols: 0,
30398     
30399     /**
30400      * @cfg {Boolean} isAutoInitial defalut true
30401      */   
30402     isAutoInitial : true, 
30403     
30404     containerWidth: 0,
30405     
30406     /**
30407      * @cfg {Boolean} isHorizontal defalut false
30408      */   
30409     isHorizontal : false, 
30410
30411     currentSize : null,
30412     
30413     tag: 'div',
30414     
30415     cls: '',
30416     
30417     bricks: null, //CompositeElement
30418     
30419     cols : 1,
30420     
30421     _isLayoutInited : false,
30422     
30423 //    isAlternative : false, // only use for vertical layout...
30424     
30425     /**
30426      * @cfg {Number} alternativePadWidth padding below box..
30427      */   
30428     alternativePadWidth : 50,
30429     
30430     selectedBrick : [],
30431     
30432     getAutoCreate : function(){
30433         
30434         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30435         
30436         var cfg = {
30437             tag: this.tag,
30438             cls: 'blog-masonary-wrapper ' + this.cls,
30439             cn : {
30440                 cls : 'mas-boxes masonary'
30441             }
30442         };
30443         
30444         return cfg;
30445     },
30446     
30447     getChildContainer: function( )
30448     {
30449         if (this.boxesEl) {
30450             return this.boxesEl;
30451         }
30452         
30453         this.boxesEl = this.el.select('.mas-boxes').first();
30454         
30455         return this.boxesEl;
30456     },
30457     
30458     
30459     initEvents : function()
30460     {
30461         var _this = this;
30462         
30463         if(this.isAutoInitial){
30464             Roo.log('hook children rendered');
30465             this.on('childrenrendered', function() {
30466                 Roo.log('children rendered');
30467                 _this.initial();
30468             } ,this);
30469         }
30470     },
30471     
30472     initial : function()
30473     {
30474         this.selectedBrick = [];
30475         
30476         this.currentSize = this.el.getBox(true);
30477         
30478         Roo.EventManager.onWindowResize(this.resize, this); 
30479
30480         if(!this.isAutoInitial){
30481             this.layout();
30482             return;
30483         }
30484         
30485         this.layout();
30486         
30487         return;
30488         //this.layout.defer(500,this);
30489         
30490     },
30491     
30492     resize : function()
30493     {
30494         var cs = this.el.getBox(true);
30495         
30496         if (
30497                 this.currentSize.width == cs.width && 
30498                 this.currentSize.x == cs.x && 
30499                 this.currentSize.height == cs.height && 
30500                 this.currentSize.y == cs.y 
30501         ) {
30502             Roo.log("no change in with or X or Y");
30503             return;
30504         }
30505         
30506         this.currentSize = cs;
30507         
30508         this.layout();
30509         
30510     },
30511     
30512     layout : function()
30513     {   
30514         this._resetLayout();
30515         
30516         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30517         
30518         this.layoutItems( isInstant );
30519       
30520         this._isLayoutInited = true;
30521         
30522         this.fireEvent('layout', this);
30523         
30524     },
30525     
30526     _resetLayout : function()
30527     {
30528         if(this.isHorizontal){
30529             this.horizontalMeasureColumns();
30530             return;
30531         }
30532         
30533         this.verticalMeasureColumns();
30534         
30535     },
30536     
30537     verticalMeasureColumns : function()
30538     {
30539         this.getContainerWidth();
30540         
30541 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30542 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30543 //            return;
30544 //        }
30545         
30546         var boxWidth = this.boxWidth + this.padWidth;
30547         
30548         if(this.containerWidth < this.boxWidth){
30549             boxWidth = this.containerWidth
30550         }
30551         
30552         var containerWidth = this.containerWidth;
30553         
30554         var cols = Math.floor(containerWidth / boxWidth);
30555         
30556         this.cols = Math.max( cols, 1 );
30557         
30558         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30559         
30560         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30561         
30562         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30563         
30564         this.colWidth = boxWidth + avail - this.padWidth;
30565         
30566         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30567         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30568     },
30569     
30570     horizontalMeasureColumns : function()
30571     {
30572         this.getContainerWidth();
30573         
30574         var boxWidth = this.boxWidth;
30575         
30576         if(this.containerWidth < boxWidth){
30577             boxWidth = this.containerWidth;
30578         }
30579         
30580         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30581         
30582         this.el.setHeight(boxWidth);
30583         
30584     },
30585     
30586     getContainerWidth : function()
30587     {
30588         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30589     },
30590     
30591     layoutItems : function( isInstant )
30592     {
30593         Roo.log(this.bricks);
30594         
30595         var items = Roo.apply([], this.bricks);
30596         
30597         if(this.isHorizontal){
30598             this._horizontalLayoutItems( items , isInstant );
30599             return;
30600         }
30601         
30602 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30603 //            this._verticalAlternativeLayoutItems( items , isInstant );
30604 //            return;
30605 //        }
30606         
30607         this._verticalLayoutItems( items , isInstant );
30608         
30609     },
30610     
30611     _verticalLayoutItems : function ( items , isInstant)
30612     {
30613         if ( !items || !items.length ) {
30614             return;
30615         }
30616         
30617         var standard = [
30618             ['xs', 'xs', 'xs', 'tall'],
30619             ['xs', 'xs', 'tall'],
30620             ['xs', 'xs', 'sm'],
30621             ['xs', 'xs', 'xs'],
30622             ['xs', 'tall'],
30623             ['xs', 'sm'],
30624             ['xs', 'xs'],
30625             ['xs'],
30626             
30627             ['sm', 'xs', 'xs'],
30628             ['sm', 'xs'],
30629             ['sm'],
30630             
30631             ['tall', 'xs', 'xs', 'xs'],
30632             ['tall', 'xs', 'xs'],
30633             ['tall', 'xs'],
30634             ['tall']
30635             
30636         ];
30637         
30638         var queue = [];
30639         
30640         var boxes = [];
30641         
30642         var box = [];
30643         
30644         Roo.each(items, function(item, k){
30645             
30646             switch (item.size) {
30647                 // these layouts take up a full box,
30648                 case 'md' :
30649                 case 'md-left' :
30650                 case 'md-right' :
30651                 case 'wide' :
30652                     
30653                     if(box.length){
30654                         boxes.push(box);
30655                         box = [];
30656                     }
30657                     
30658                     boxes.push([item]);
30659                     
30660                     break;
30661                     
30662                 case 'xs' :
30663                 case 'sm' :
30664                 case 'tall' :
30665                     
30666                     box.push(item);
30667                     
30668                     break;
30669                 default :
30670                     break;
30671                     
30672             }
30673             
30674         }, this);
30675         
30676         if(box.length){
30677             boxes.push(box);
30678             box = [];
30679         }
30680         
30681         var filterPattern = function(box, length)
30682         {
30683             if(!box.length){
30684                 return;
30685             }
30686             
30687             var match = false;
30688             
30689             var pattern = box.slice(0, length);
30690             
30691             var format = [];
30692             
30693             Roo.each(pattern, function(i){
30694                 format.push(i.size);
30695             }, this);
30696             
30697             Roo.each(standard, function(s){
30698                 
30699                 if(String(s) != String(format)){
30700                     return;
30701                 }
30702                 
30703                 match = true;
30704                 return false;
30705                 
30706             }, this);
30707             
30708             if(!match && length == 1){
30709                 return;
30710             }
30711             
30712             if(!match){
30713                 filterPattern(box, length - 1);
30714                 return;
30715             }
30716                 
30717             queue.push(pattern);
30718
30719             box = box.slice(length, box.length);
30720
30721             filterPattern(box, 4);
30722
30723             return;
30724             
30725         }
30726         
30727         Roo.each(boxes, function(box, k){
30728             
30729             if(!box.length){
30730                 return;
30731             }
30732             
30733             if(box.length == 1){
30734                 queue.push(box);
30735                 return;
30736             }
30737             
30738             filterPattern(box, 4);
30739             
30740         }, this);
30741         
30742         this._processVerticalLayoutQueue( queue, isInstant );
30743         
30744     },
30745     
30746 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30747 //    {
30748 //        if ( !items || !items.length ) {
30749 //            return;
30750 //        }
30751 //
30752 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30753 //        
30754 //    },
30755     
30756     _horizontalLayoutItems : function ( items , isInstant)
30757     {
30758         if ( !items || !items.length || items.length < 3) {
30759             return;
30760         }
30761         
30762         items.reverse();
30763         
30764         var eItems = items.slice(0, 3);
30765         
30766         items = items.slice(3, items.length);
30767         
30768         var standard = [
30769             ['xs', 'xs', 'xs', 'wide'],
30770             ['xs', 'xs', 'wide'],
30771             ['xs', 'xs', 'sm'],
30772             ['xs', 'xs', 'xs'],
30773             ['xs', 'wide'],
30774             ['xs', 'sm'],
30775             ['xs', 'xs'],
30776             ['xs'],
30777             
30778             ['sm', 'xs', 'xs'],
30779             ['sm', 'xs'],
30780             ['sm'],
30781             
30782             ['wide', 'xs', 'xs', 'xs'],
30783             ['wide', 'xs', 'xs'],
30784             ['wide', 'xs'],
30785             ['wide'],
30786             
30787             ['wide-thin']
30788         ];
30789         
30790         var queue = [];
30791         
30792         var boxes = [];
30793         
30794         var box = [];
30795         
30796         Roo.each(items, function(item, k){
30797             
30798             switch (item.size) {
30799                 case 'md' :
30800                 case 'md-left' :
30801                 case 'md-right' :
30802                 case 'tall' :
30803                     
30804                     if(box.length){
30805                         boxes.push(box);
30806                         box = [];
30807                     }
30808                     
30809                     boxes.push([item]);
30810                     
30811                     break;
30812                     
30813                 case 'xs' :
30814                 case 'sm' :
30815                 case 'wide' :
30816                 case 'wide-thin' :
30817                     
30818                     box.push(item);
30819                     
30820                     break;
30821                 default :
30822                     break;
30823                     
30824             }
30825             
30826         }, this);
30827         
30828         if(box.length){
30829             boxes.push(box);
30830             box = [];
30831         }
30832         
30833         var filterPattern = function(box, length)
30834         {
30835             if(!box.length){
30836                 return;
30837             }
30838             
30839             var match = false;
30840             
30841             var pattern = box.slice(0, length);
30842             
30843             var format = [];
30844             
30845             Roo.each(pattern, function(i){
30846                 format.push(i.size);
30847             }, this);
30848             
30849             Roo.each(standard, function(s){
30850                 
30851                 if(String(s) != String(format)){
30852                     return;
30853                 }
30854                 
30855                 match = true;
30856                 return false;
30857                 
30858             }, this);
30859             
30860             if(!match && length == 1){
30861                 return;
30862             }
30863             
30864             if(!match){
30865                 filterPattern(box, length - 1);
30866                 return;
30867             }
30868                 
30869             queue.push(pattern);
30870
30871             box = box.slice(length, box.length);
30872
30873             filterPattern(box, 4);
30874
30875             return;
30876             
30877         }
30878         
30879         Roo.each(boxes, function(box, k){
30880             
30881             if(!box.length){
30882                 return;
30883             }
30884             
30885             if(box.length == 1){
30886                 queue.push(box);
30887                 return;
30888             }
30889             
30890             filterPattern(box, 4);
30891             
30892         }, this);
30893         
30894         
30895         var prune = [];
30896         
30897         var pos = this.el.getBox(true);
30898         
30899         var minX = pos.x;
30900         
30901         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30902         
30903         var hit_end = false;
30904         
30905         Roo.each(queue, function(box){
30906             
30907             if(hit_end){
30908                 
30909                 Roo.each(box, function(b){
30910                 
30911                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30912                     b.el.hide();
30913
30914                 }, this);
30915
30916                 return;
30917             }
30918             
30919             var mx = 0;
30920             
30921             Roo.each(box, function(b){
30922                 
30923                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30924                 b.el.show();
30925
30926                 mx = Math.max(mx, b.x);
30927                 
30928             }, this);
30929             
30930             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30931             
30932             if(maxX < minX){
30933                 
30934                 Roo.each(box, function(b){
30935                 
30936                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30937                     b.el.hide();
30938                     
30939                 }, this);
30940                 
30941                 hit_end = true;
30942                 
30943                 return;
30944             }
30945             
30946             prune.push(box);
30947             
30948         }, this);
30949         
30950         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30951     },
30952     
30953     /** Sets position of item in DOM
30954     * @param {Element} item
30955     * @param {Number} x - horizontal position
30956     * @param {Number} y - vertical position
30957     * @param {Boolean} isInstant - disables transitions
30958     */
30959     _processVerticalLayoutQueue : function( queue, isInstant )
30960     {
30961         var pos = this.el.getBox(true);
30962         var x = pos.x;
30963         var y = pos.y;
30964         var maxY = [];
30965         
30966         for (var i = 0; i < this.cols; i++){
30967             maxY[i] = pos.y;
30968         }
30969         
30970         Roo.each(queue, function(box, k){
30971             
30972             var col = k % this.cols;
30973             
30974             Roo.each(box, function(b,kk){
30975                 
30976                 b.el.position('absolute');
30977                 
30978                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30979                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30980                 
30981                 if(b.size == 'md-left' || b.size == 'md-right'){
30982                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30983                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30984                 }
30985                 
30986                 b.el.setWidth(width);
30987                 b.el.setHeight(height);
30988                 // iframe?
30989                 b.el.select('iframe',true).setSize(width,height);
30990                 
30991             }, this);
30992             
30993             for (var i = 0; i < this.cols; i++){
30994                 
30995                 if(maxY[i] < maxY[col]){
30996                     col = i;
30997                     continue;
30998                 }
30999                 
31000                 col = Math.min(col, i);
31001                 
31002             }
31003             
31004             x = pos.x + col * (this.colWidth + this.padWidth);
31005             
31006             y = maxY[col];
31007             
31008             var positions = [];
31009             
31010             switch (box.length){
31011                 case 1 :
31012                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31013                     break;
31014                 case 2 :
31015                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31016                     break;
31017                 case 3 :
31018                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31019                     break;
31020                 case 4 :
31021                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31022                     break;
31023                 default :
31024                     break;
31025             }
31026             
31027             Roo.each(box, function(b,kk){
31028                 
31029                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31030                 
31031                 var sz = b.el.getSize();
31032                 
31033                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31034                 
31035             }, this);
31036             
31037         }, this);
31038         
31039         var mY = 0;
31040         
31041         for (var i = 0; i < this.cols; i++){
31042             mY = Math.max(mY, maxY[i]);
31043         }
31044         
31045         this.el.setHeight(mY - pos.y);
31046         
31047     },
31048     
31049 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31050 //    {
31051 //        var pos = this.el.getBox(true);
31052 //        var x = pos.x;
31053 //        var y = pos.y;
31054 //        var maxX = pos.right;
31055 //        
31056 //        var maxHeight = 0;
31057 //        
31058 //        Roo.each(items, function(item, k){
31059 //            
31060 //            var c = k % 2;
31061 //            
31062 //            item.el.position('absolute');
31063 //                
31064 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31065 //
31066 //            item.el.setWidth(width);
31067 //
31068 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31069 //
31070 //            item.el.setHeight(height);
31071 //            
31072 //            if(c == 0){
31073 //                item.el.setXY([x, y], isInstant ? false : true);
31074 //            } else {
31075 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31076 //            }
31077 //            
31078 //            y = y + height + this.alternativePadWidth;
31079 //            
31080 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31081 //            
31082 //        }, this);
31083 //        
31084 //        this.el.setHeight(maxHeight);
31085 //        
31086 //    },
31087     
31088     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31089     {
31090         var pos = this.el.getBox(true);
31091         
31092         var minX = pos.x;
31093         var minY = pos.y;
31094         
31095         var maxX = pos.right;
31096         
31097         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31098         
31099         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31100         
31101         Roo.each(queue, function(box, k){
31102             
31103             Roo.each(box, function(b, kk){
31104                 
31105                 b.el.position('absolute');
31106                 
31107                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31108                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31109                 
31110                 if(b.size == 'md-left' || b.size == 'md-right'){
31111                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31112                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31113                 }
31114                 
31115                 b.el.setWidth(width);
31116                 b.el.setHeight(height);
31117                 
31118             }, this);
31119             
31120             if(!box.length){
31121                 return;
31122             }
31123             
31124             var positions = [];
31125             
31126             switch (box.length){
31127                 case 1 :
31128                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31129                     break;
31130                 case 2 :
31131                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31132                     break;
31133                 case 3 :
31134                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31135                     break;
31136                 case 4 :
31137                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31138                     break;
31139                 default :
31140                     break;
31141             }
31142             
31143             Roo.each(box, function(b,kk){
31144                 
31145                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31146                 
31147                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31148                 
31149             }, this);
31150             
31151         }, this);
31152         
31153     },
31154     
31155     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31156     {
31157         Roo.each(eItems, function(b,k){
31158             
31159             b.size = (k == 0) ? 'sm' : 'xs';
31160             b.x = (k == 0) ? 2 : 1;
31161             b.y = (k == 0) ? 2 : 1;
31162             
31163             b.el.position('absolute');
31164             
31165             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31166                 
31167             b.el.setWidth(width);
31168             
31169             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31170             
31171             b.el.setHeight(height);
31172             
31173         }, this);
31174
31175         var positions = [];
31176         
31177         positions.push({
31178             x : maxX - this.unitWidth * 2 - this.gutter,
31179             y : minY
31180         });
31181         
31182         positions.push({
31183             x : maxX - this.unitWidth,
31184             y : minY + (this.unitWidth + this.gutter) * 2
31185         });
31186         
31187         positions.push({
31188             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31189             y : minY
31190         });
31191         
31192         Roo.each(eItems, function(b,k){
31193             
31194             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31195
31196         }, this);
31197         
31198     },
31199     
31200     getVerticalOneBoxColPositions : function(x, y, box)
31201     {
31202         var pos = [];
31203         
31204         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31205         
31206         if(box[0].size == 'md-left'){
31207             rand = 0;
31208         }
31209         
31210         if(box[0].size == 'md-right'){
31211             rand = 1;
31212         }
31213         
31214         pos.push({
31215             x : x + (this.unitWidth + this.gutter) * rand,
31216             y : y
31217         });
31218         
31219         return pos;
31220     },
31221     
31222     getVerticalTwoBoxColPositions : function(x, y, box)
31223     {
31224         var pos = [];
31225         
31226         if(box[0].size == 'xs'){
31227             
31228             pos.push({
31229                 x : x,
31230                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31231             });
31232
31233             pos.push({
31234                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31235                 y : y
31236             });
31237             
31238             return pos;
31239             
31240         }
31241         
31242         pos.push({
31243             x : x,
31244             y : y
31245         });
31246
31247         pos.push({
31248             x : x + (this.unitWidth + this.gutter) * 2,
31249             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31250         });
31251         
31252         return pos;
31253         
31254     },
31255     
31256     getVerticalThreeBoxColPositions : function(x, y, box)
31257     {
31258         var pos = [];
31259         
31260         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31261             
31262             pos.push({
31263                 x : x,
31264                 y : y
31265             });
31266
31267             pos.push({
31268                 x : x + (this.unitWidth + this.gutter) * 1,
31269                 y : y
31270             });
31271             
31272             pos.push({
31273                 x : x + (this.unitWidth + this.gutter) * 2,
31274                 y : y
31275             });
31276             
31277             return pos;
31278             
31279         }
31280         
31281         if(box[0].size == 'xs' && box[1].size == 'xs'){
31282             
31283             pos.push({
31284                 x : x,
31285                 y : y
31286             });
31287
31288             pos.push({
31289                 x : x,
31290                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31291             });
31292             
31293             pos.push({
31294                 x : x + (this.unitWidth + this.gutter) * 1,
31295                 y : y
31296             });
31297             
31298             return pos;
31299             
31300         }
31301         
31302         pos.push({
31303             x : x,
31304             y : y
31305         });
31306
31307         pos.push({
31308             x : x + (this.unitWidth + this.gutter) * 2,
31309             y : y
31310         });
31311
31312         pos.push({
31313             x : x + (this.unitWidth + this.gutter) * 2,
31314             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31315         });
31316             
31317         return pos;
31318         
31319     },
31320     
31321     getVerticalFourBoxColPositions : function(x, y, box)
31322     {
31323         var pos = [];
31324         
31325         if(box[0].size == 'xs'){
31326             
31327             pos.push({
31328                 x : x,
31329                 y : y
31330             });
31331
31332             pos.push({
31333                 x : x,
31334                 y : y + (this.unitHeight + this.gutter) * 1
31335             });
31336             
31337             pos.push({
31338                 x : x,
31339                 y : y + (this.unitHeight + this.gutter) * 2
31340             });
31341             
31342             pos.push({
31343                 x : x + (this.unitWidth + this.gutter) * 1,
31344                 y : y
31345             });
31346             
31347             return pos;
31348             
31349         }
31350         
31351         pos.push({
31352             x : x,
31353             y : y
31354         });
31355
31356         pos.push({
31357             x : x + (this.unitWidth + this.gutter) * 2,
31358             y : y
31359         });
31360
31361         pos.push({
31362             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31363             y : y + (this.unitHeight + this.gutter) * 1
31364         });
31365
31366         pos.push({
31367             x : x + (this.unitWidth + this.gutter) * 2,
31368             y : y + (this.unitWidth + this.gutter) * 2
31369         });
31370
31371         return pos;
31372         
31373     },
31374     
31375     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31376     {
31377         var pos = [];
31378         
31379         if(box[0].size == 'md-left'){
31380             pos.push({
31381                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31382                 y : minY
31383             });
31384             
31385             return pos;
31386         }
31387         
31388         if(box[0].size == 'md-right'){
31389             pos.push({
31390                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31391                 y : minY + (this.unitWidth + this.gutter) * 1
31392             });
31393             
31394             return pos;
31395         }
31396         
31397         var rand = Math.floor(Math.random() * (4 - box[0].y));
31398         
31399         pos.push({
31400             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31401             y : minY + (this.unitWidth + this.gutter) * rand
31402         });
31403         
31404         return pos;
31405         
31406     },
31407     
31408     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31409     {
31410         var pos = [];
31411         
31412         if(box[0].size == 'xs'){
31413             
31414             pos.push({
31415                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31416                 y : minY
31417             });
31418
31419             pos.push({
31420                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31421                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31422             });
31423             
31424             return pos;
31425             
31426         }
31427         
31428         pos.push({
31429             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31430             y : minY
31431         });
31432
31433         pos.push({
31434             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31435             y : minY + (this.unitWidth + this.gutter) * 2
31436         });
31437         
31438         return pos;
31439         
31440     },
31441     
31442     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31443     {
31444         var pos = [];
31445         
31446         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31447             
31448             pos.push({
31449                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31450                 y : minY
31451             });
31452
31453             pos.push({
31454                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31455                 y : minY + (this.unitWidth + this.gutter) * 1
31456             });
31457             
31458             pos.push({
31459                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31460                 y : minY + (this.unitWidth + this.gutter) * 2
31461             });
31462             
31463             return pos;
31464             
31465         }
31466         
31467         if(box[0].size == 'xs' && box[1].size == 'xs'){
31468             
31469             pos.push({
31470                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31471                 y : minY
31472             });
31473
31474             pos.push({
31475                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31476                 y : minY
31477             });
31478             
31479             pos.push({
31480                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31481                 y : minY + (this.unitWidth + this.gutter) * 1
31482             });
31483             
31484             return pos;
31485             
31486         }
31487         
31488         pos.push({
31489             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31490             y : minY
31491         });
31492
31493         pos.push({
31494             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31495             y : minY + (this.unitWidth + this.gutter) * 2
31496         });
31497
31498         pos.push({
31499             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31500             y : minY + (this.unitWidth + this.gutter) * 2
31501         });
31502             
31503         return pos;
31504         
31505     },
31506     
31507     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31508     {
31509         var pos = [];
31510         
31511         if(box[0].size == 'xs'){
31512             
31513             pos.push({
31514                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31515                 y : minY
31516             });
31517
31518             pos.push({
31519                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31520                 y : minY
31521             });
31522             
31523             pos.push({
31524                 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),
31525                 y : minY
31526             });
31527             
31528             pos.push({
31529                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31530                 y : minY + (this.unitWidth + this.gutter) * 1
31531             });
31532             
31533             return pos;
31534             
31535         }
31536         
31537         pos.push({
31538             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31539             y : minY
31540         });
31541         
31542         pos.push({
31543             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31544             y : minY + (this.unitWidth + this.gutter) * 2
31545         });
31546         
31547         pos.push({
31548             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31549             y : minY + (this.unitWidth + this.gutter) * 2
31550         });
31551         
31552         pos.push({
31553             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),
31554             y : minY + (this.unitWidth + this.gutter) * 2
31555         });
31556
31557         return pos;
31558         
31559     },
31560     
31561     /**
31562     * remove a Masonry Brick
31563     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31564     */
31565     removeBrick : function(brick_id)
31566     {
31567         if (!brick_id) {
31568             return;
31569         }
31570         
31571         for (var i = 0; i<this.bricks.length; i++) {
31572             if (this.bricks[i].id == brick_id) {
31573                 this.bricks.splice(i,1);
31574                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31575                 this.initial();
31576             }
31577         }
31578     },
31579     
31580     /**
31581     * adds a Masonry Brick
31582     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31583     */
31584     addBrick : function(cfg)
31585     {
31586         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31587         //this.register(cn);
31588         cn.parentId = this.id;
31589         cn.onRender(this.el, null);
31590         return cn;
31591     },
31592     
31593     /**
31594     * register a Masonry Brick
31595     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31596     */
31597     
31598     register : function(brick)
31599     {
31600         this.bricks.push(brick);
31601         brick.masonryId = this.id;
31602     },
31603     
31604     /**
31605     * clear all the Masonry Brick
31606     */
31607     clearAll : function()
31608     {
31609         this.bricks = [];
31610         //this.getChildContainer().dom.innerHTML = "";
31611         this.el.dom.innerHTML = '';
31612     },
31613     
31614     getSelected : function()
31615     {
31616         if (!this.selectedBrick) {
31617             return false;
31618         }
31619         
31620         return this.selectedBrick;
31621     }
31622 });
31623
31624 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31625     
31626     groups: {},
31627      /**
31628     * register a Masonry Layout
31629     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31630     */
31631     
31632     register : function(layout)
31633     {
31634         this.groups[layout.id] = layout;
31635     },
31636     /**
31637     * fetch a  Masonry Layout based on the masonry layout ID
31638     * @param {string} the masonry layout to add
31639     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31640     */
31641     
31642     get: function(layout_id) {
31643         if (typeof(this.groups[layout_id]) == 'undefined') {
31644             return false;
31645         }
31646         return this.groups[layout_id] ;
31647     }
31648     
31649     
31650     
31651 });
31652
31653  
31654
31655  /**
31656  *
31657  * This is based on 
31658  * http://masonry.desandro.com
31659  *
31660  * The idea is to render all the bricks based on vertical width...
31661  *
31662  * The original code extends 'outlayer' - we might need to use that....
31663  * 
31664  */
31665
31666
31667 /**
31668  * @class Roo.bootstrap.LayoutMasonryAuto
31669  * @extends Roo.bootstrap.Component
31670  * Bootstrap Layout Masonry class
31671  * 
31672  * @constructor
31673  * Create a new Element
31674  * @param {Object} config The config object
31675  */
31676
31677 Roo.bootstrap.LayoutMasonryAuto = function(config){
31678     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31679 };
31680
31681 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31682     
31683       /**
31684      * @cfg {Boolean} isFitWidth  - resize the width..
31685      */   
31686     isFitWidth : false,  // options..
31687     /**
31688      * @cfg {Boolean} isOriginLeft = left align?
31689      */   
31690     isOriginLeft : true,
31691     /**
31692      * @cfg {Boolean} isOriginTop = top align?
31693      */   
31694     isOriginTop : false,
31695     /**
31696      * @cfg {Boolean} isLayoutInstant = no animation?
31697      */   
31698     isLayoutInstant : false, // needed?
31699     /**
31700      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31701      */   
31702     isResizingContainer : true,
31703     /**
31704      * @cfg {Number} columnWidth  width of the columns 
31705      */   
31706     
31707     columnWidth : 0,
31708     
31709     /**
31710      * @cfg {Number} maxCols maximum number of columns
31711      */   
31712     
31713     maxCols: 0,
31714     /**
31715      * @cfg {Number} padHeight padding below box..
31716      */   
31717     
31718     padHeight : 10, 
31719     
31720     /**
31721      * @cfg {Boolean} isAutoInitial defalut true
31722      */   
31723     
31724     isAutoInitial : true, 
31725     
31726     // private?
31727     gutter : 0,
31728     
31729     containerWidth: 0,
31730     initialColumnWidth : 0,
31731     currentSize : null,
31732     
31733     colYs : null, // array.
31734     maxY : 0,
31735     padWidth: 10,
31736     
31737     
31738     tag: 'div',
31739     cls: '',
31740     bricks: null, //CompositeElement
31741     cols : 0, // array?
31742     // element : null, // wrapped now this.el
31743     _isLayoutInited : null, 
31744     
31745     
31746     getAutoCreate : function(){
31747         
31748         var cfg = {
31749             tag: this.tag,
31750             cls: 'blog-masonary-wrapper ' + this.cls,
31751             cn : {
31752                 cls : 'mas-boxes masonary'
31753             }
31754         };
31755         
31756         return cfg;
31757     },
31758     
31759     getChildContainer: function( )
31760     {
31761         if (this.boxesEl) {
31762             return this.boxesEl;
31763         }
31764         
31765         this.boxesEl = this.el.select('.mas-boxes').first();
31766         
31767         return this.boxesEl;
31768     },
31769     
31770     
31771     initEvents : function()
31772     {
31773         var _this = this;
31774         
31775         if(this.isAutoInitial){
31776             Roo.log('hook children rendered');
31777             this.on('childrenrendered', function() {
31778                 Roo.log('children rendered');
31779                 _this.initial();
31780             } ,this);
31781         }
31782         
31783     },
31784     
31785     initial : function()
31786     {
31787         this.reloadItems();
31788
31789         this.currentSize = this.el.getBox(true);
31790
31791         /// was window resize... - let's see if this works..
31792         Roo.EventManager.onWindowResize(this.resize, this); 
31793
31794         if(!this.isAutoInitial){
31795             this.layout();
31796             return;
31797         }
31798         
31799         this.layout.defer(500,this);
31800     },
31801     
31802     reloadItems: function()
31803     {
31804         this.bricks = this.el.select('.masonry-brick', true);
31805         
31806         this.bricks.each(function(b) {
31807             //Roo.log(b.getSize());
31808             if (!b.attr('originalwidth')) {
31809                 b.attr('originalwidth',  b.getSize().width);
31810             }
31811             
31812         });
31813         
31814         Roo.log(this.bricks.elements.length);
31815     },
31816     
31817     resize : function()
31818     {
31819         Roo.log('resize');
31820         var cs = this.el.getBox(true);
31821         
31822         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31823             Roo.log("no change in with or X");
31824             return;
31825         }
31826         this.currentSize = cs;
31827         this.layout();
31828     },
31829     
31830     layout : function()
31831     {
31832          Roo.log('layout');
31833         this._resetLayout();
31834         //this._manageStamps();
31835       
31836         // don't animate first layout
31837         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31838         this.layoutItems( isInstant );
31839       
31840         // flag for initalized
31841         this._isLayoutInited = true;
31842     },
31843     
31844     layoutItems : function( isInstant )
31845     {
31846         //var items = this._getItemsForLayout( this.items );
31847         // original code supports filtering layout items.. we just ignore it..
31848         
31849         this._layoutItems( this.bricks , isInstant );
31850       
31851         this._postLayout();
31852     },
31853     _layoutItems : function ( items , isInstant)
31854     {
31855        //this.fireEvent( 'layout', this, items );
31856     
31857
31858         if ( !items || !items.elements.length ) {
31859           // no items, emit event with empty array
31860             return;
31861         }
31862
31863         var queue = [];
31864         items.each(function(item) {
31865             Roo.log("layout item");
31866             Roo.log(item);
31867             // get x/y object from method
31868             var position = this._getItemLayoutPosition( item );
31869             // enqueue
31870             position.item = item;
31871             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31872             queue.push( position );
31873         }, this);
31874       
31875         this._processLayoutQueue( queue );
31876     },
31877     /** Sets position of item in DOM
31878     * @param {Element} item
31879     * @param {Number} x - horizontal position
31880     * @param {Number} y - vertical position
31881     * @param {Boolean} isInstant - disables transitions
31882     */
31883     _processLayoutQueue : function( queue )
31884     {
31885         for ( var i=0, len = queue.length; i < len; i++ ) {
31886             var obj = queue[i];
31887             obj.item.position('absolute');
31888             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31889         }
31890     },
31891       
31892     
31893     /**
31894     * Any logic you want to do after each layout,
31895     * i.e. size the container
31896     */
31897     _postLayout : function()
31898     {
31899         this.resizeContainer();
31900     },
31901     
31902     resizeContainer : function()
31903     {
31904         if ( !this.isResizingContainer ) {
31905             return;
31906         }
31907         var size = this._getContainerSize();
31908         if ( size ) {
31909             this.el.setSize(size.width,size.height);
31910             this.boxesEl.setSize(size.width,size.height);
31911         }
31912     },
31913     
31914     
31915     
31916     _resetLayout : function()
31917     {
31918         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31919         this.colWidth = this.el.getWidth();
31920         //this.gutter = this.el.getWidth(); 
31921         
31922         this.measureColumns();
31923
31924         // reset column Y
31925         var i = this.cols;
31926         this.colYs = [];
31927         while (i--) {
31928             this.colYs.push( 0 );
31929         }
31930     
31931         this.maxY = 0;
31932     },
31933
31934     measureColumns : function()
31935     {
31936         this.getContainerWidth();
31937       // if columnWidth is 0, default to outerWidth of first item
31938         if ( !this.columnWidth ) {
31939             var firstItem = this.bricks.first();
31940             Roo.log(firstItem);
31941             this.columnWidth  = this.containerWidth;
31942             if (firstItem && firstItem.attr('originalwidth') ) {
31943                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31944             }
31945             // columnWidth fall back to item of first element
31946             Roo.log("set column width?");
31947                         this.initialColumnWidth = this.columnWidth  ;
31948
31949             // if first elem has no width, default to size of container
31950             
31951         }
31952         
31953         
31954         if (this.initialColumnWidth) {
31955             this.columnWidth = this.initialColumnWidth;
31956         }
31957         
31958         
31959             
31960         // column width is fixed at the top - however if container width get's smaller we should
31961         // reduce it...
31962         
31963         // this bit calcs how man columns..
31964             
31965         var columnWidth = this.columnWidth += this.gutter;
31966       
31967         // calculate columns
31968         var containerWidth = this.containerWidth + this.gutter;
31969         
31970         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31971         // fix rounding errors, typically with gutters
31972         var excess = columnWidth - containerWidth % columnWidth;
31973         
31974         
31975         // if overshoot is less than a pixel, round up, otherwise floor it
31976         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31977         cols = Math[ mathMethod ]( cols );
31978         this.cols = Math.max( cols, 1 );
31979         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31980         
31981          // padding positioning..
31982         var totalColWidth = this.cols * this.columnWidth;
31983         var padavail = this.containerWidth - totalColWidth;
31984         // so for 2 columns - we need 3 'pads'
31985         
31986         var padNeeded = (1+this.cols) * this.padWidth;
31987         
31988         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31989         
31990         this.columnWidth += padExtra
31991         //this.padWidth = Math.floor(padavail /  ( this.cols));
31992         
31993         // adjust colum width so that padding is fixed??
31994         
31995         // we have 3 columns ... total = width * 3
31996         // we have X left over... that should be used by 
31997         
31998         //if (this.expandC) {
31999             
32000         //}
32001         
32002         
32003         
32004     },
32005     
32006     getContainerWidth : function()
32007     {
32008        /* // container is parent if fit width
32009         var container = this.isFitWidth ? this.element.parentNode : this.element;
32010         // check that this.size and size are there
32011         // IE8 triggers resize on body size change, so they might not be
32012         
32013         var size = getSize( container );  //FIXME
32014         this.containerWidth = size && size.innerWidth; //FIXME
32015         */
32016          
32017         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32018         
32019     },
32020     
32021     _getItemLayoutPosition : function( item )  // what is item?
32022     {
32023         // we resize the item to our columnWidth..
32024       
32025         item.setWidth(this.columnWidth);
32026         item.autoBoxAdjust  = false;
32027         
32028         var sz = item.getSize();
32029  
32030         // how many columns does this brick span
32031         var remainder = this.containerWidth % this.columnWidth;
32032         
32033         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32034         // round if off by 1 pixel, otherwise use ceil
32035         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32036         colSpan = Math.min( colSpan, this.cols );
32037         
32038         // normally this should be '1' as we dont' currently allow multi width columns..
32039         
32040         var colGroup = this._getColGroup( colSpan );
32041         // get the minimum Y value from the columns
32042         var minimumY = Math.min.apply( Math, colGroup );
32043         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32044         
32045         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32046          
32047         // position the brick
32048         var position = {
32049             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32050             y: this.currentSize.y + minimumY + this.padHeight
32051         };
32052         
32053         Roo.log(position);
32054         // apply setHeight to necessary columns
32055         var setHeight = minimumY + sz.height + this.padHeight;
32056         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32057         
32058         var setSpan = this.cols + 1 - colGroup.length;
32059         for ( var i = 0; i < setSpan; i++ ) {
32060           this.colYs[ shortColIndex + i ] = setHeight ;
32061         }
32062       
32063         return position;
32064     },
32065     
32066     /**
32067      * @param {Number} colSpan - number of columns the element spans
32068      * @returns {Array} colGroup
32069      */
32070     _getColGroup : function( colSpan )
32071     {
32072         if ( colSpan < 2 ) {
32073           // if brick spans only one column, use all the column Ys
32074           return this.colYs;
32075         }
32076       
32077         var colGroup = [];
32078         // how many different places could this brick fit horizontally
32079         var groupCount = this.cols + 1 - colSpan;
32080         // for each group potential horizontal position
32081         for ( var i = 0; i < groupCount; i++ ) {
32082           // make an array of colY values for that one group
32083           var groupColYs = this.colYs.slice( i, i + colSpan );
32084           // and get the max value of the array
32085           colGroup[i] = Math.max.apply( Math, groupColYs );
32086         }
32087         return colGroup;
32088     },
32089     /*
32090     _manageStamp : function( stamp )
32091     {
32092         var stampSize =  stamp.getSize();
32093         var offset = stamp.getBox();
32094         // get the columns that this stamp affects
32095         var firstX = this.isOriginLeft ? offset.x : offset.right;
32096         var lastX = firstX + stampSize.width;
32097         var firstCol = Math.floor( firstX / this.columnWidth );
32098         firstCol = Math.max( 0, firstCol );
32099         
32100         var lastCol = Math.floor( lastX / this.columnWidth );
32101         // lastCol should not go over if multiple of columnWidth #425
32102         lastCol -= lastX % this.columnWidth ? 0 : 1;
32103         lastCol = Math.min( this.cols - 1, lastCol );
32104         
32105         // set colYs to bottom of the stamp
32106         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32107             stampSize.height;
32108             
32109         for ( var i = firstCol; i <= lastCol; i++ ) {
32110           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32111         }
32112     },
32113     */
32114     
32115     _getContainerSize : function()
32116     {
32117         this.maxY = Math.max.apply( Math, this.colYs );
32118         var size = {
32119             height: this.maxY
32120         };
32121       
32122         if ( this.isFitWidth ) {
32123             size.width = this._getContainerFitWidth();
32124         }
32125       
32126         return size;
32127     },
32128     
32129     _getContainerFitWidth : function()
32130     {
32131         var unusedCols = 0;
32132         // count unused columns
32133         var i = this.cols;
32134         while ( --i ) {
32135           if ( this.colYs[i] !== 0 ) {
32136             break;
32137           }
32138           unusedCols++;
32139         }
32140         // fit container to columns that have been used
32141         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32142     },
32143     
32144     needsResizeLayout : function()
32145     {
32146         var previousWidth = this.containerWidth;
32147         this.getContainerWidth();
32148         return previousWidth !== this.containerWidth;
32149     }
32150  
32151 });
32152
32153  
32154
32155  /*
32156  * - LGPL
32157  *
32158  * element
32159  * 
32160  */
32161
32162 /**
32163  * @class Roo.bootstrap.MasonryBrick
32164  * @extends Roo.bootstrap.Component
32165  * Bootstrap MasonryBrick class
32166  * 
32167  * @constructor
32168  * Create a new MasonryBrick
32169  * @param {Object} config The config object
32170  */
32171
32172 Roo.bootstrap.MasonryBrick = function(config){
32173     
32174     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32175     
32176     Roo.bootstrap.MasonryBrick.register(this);
32177     
32178     this.addEvents({
32179         // raw events
32180         /**
32181          * @event click
32182          * When a MasonryBrick is clcik
32183          * @param {Roo.bootstrap.MasonryBrick} this
32184          * @param {Roo.EventObject} e
32185          */
32186         "click" : true
32187     });
32188 };
32189
32190 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32191     
32192     /**
32193      * @cfg {String} title
32194      */   
32195     title : '',
32196     /**
32197      * @cfg {String} html
32198      */   
32199     html : '',
32200     /**
32201      * @cfg {String} bgimage
32202      */   
32203     bgimage : '',
32204     /**
32205      * @cfg {String} videourl
32206      */   
32207     videourl : '',
32208     /**
32209      * @cfg {String} cls
32210      */   
32211     cls : '',
32212     /**
32213      * @cfg {String} href
32214      */   
32215     href : '',
32216     /**
32217      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32218      */   
32219     size : 'xs',
32220     
32221     /**
32222      * @cfg {String} placetitle (center|bottom)
32223      */   
32224     placetitle : '',
32225     
32226     /**
32227      * @cfg {Boolean} isFitContainer defalut true
32228      */   
32229     isFitContainer : true, 
32230     
32231     /**
32232      * @cfg {Boolean} preventDefault defalut false
32233      */   
32234     preventDefault : false, 
32235     
32236     /**
32237      * @cfg {Boolean} inverse defalut false
32238      */   
32239     maskInverse : false, 
32240     
32241     getAutoCreate : function()
32242     {
32243         if(!this.isFitContainer){
32244             return this.getSplitAutoCreate();
32245         }
32246         
32247         var cls = 'masonry-brick masonry-brick-full';
32248         
32249         if(this.href.length){
32250             cls += ' masonry-brick-link';
32251         }
32252         
32253         if(this.bgimage.length){
32254             cls += ' masonry-brick-image';
32255         }
32256         
32257         if(this.maskInverse){
32258             cls += ' mask-inverse';
32259         }
32260         
32261         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32262             cls += ' enable-mask';
32263         }
32264         
32265         if(this.size){
32266             cls += ' masonry-' + this.size + '-brick';
32267         }
32268         
32269         if(this.placetitle.length){
32270             
32271             switch (this.placetitle) {
32272                 case 'center' :
32273                     cls += ' masonry-center-title';
32274                     break;
32275                 case 'bottom' :
32276                     cls += ' masonry-bottom-title';
32277                     break;
32278                 default:
32279                     break;
32280             }
32281             
32282         } else {
32283             if(!this.html.length && !this.bgimage.length){
32284                 cls += ' masonry-center-title';
32285             }
32286
32287             if(!this.html.length && this.bgimage.length){
32288                 cls += ' masonry-bottom-title';
32289             }
32290         }
32291         
32292         if(this.cls){
32293             cls += ' ' + this.cls;
32294         }
32295         
32296         var cfg = {
32297             tag: (this.href.length) ? 'a' : 'div',
32298             cls: cls,
32299             cn: [
32300                 {
32301                     tag: 'div',
32302                     cls: 'masonry-brick-mask'
32303                 },
32304                 {
32305                     tag: 'div',
32306                     cls: 'masonry-brick-paragraph',
32307                     cn: []
32308                 }
32309             ]
32310         };
32311         
32312         if(this.href.length){
32313             cfg.href = this.href;
32314         }
32315         
32316         var cn = cfg.cn[1].cn;
32317         
32318         if(this.title.length){
32319             cn.push({
32320                 tag: 'h4',
32321                 cls: 'masonry-brick-title',
32322                 html: this.title
32323             });
32324         }
32325         
32326         if(this.html.length){
32327             cn.push({
32328                 tag: 'p',
32329                 cls: 'masonry-brick-text',
32330                 html: this.html
32331             });
32332         }
32333         
32334         if (!this.title.length && !this.html.length) {
32335             cfg.cn[1].cls += ' hide';
32336         }
32337         
32338         if(this.bgimage.length){
32339             cfg.cn.push({
32340                 tag: 'img',
32341                 cls: 'masonry-brick-image-view',
32342                 src: this.bgimage
32343             });
32344         }
32345         
32346         if(this.videourl.length){
32347             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32348             // youtube support only?
32349             cfg.cn.push({
32350                 tag: 'iframe',
32351                 cls: 'masonry-brick-image-view',
32352                 src: vurl,
32353                 frameborder : 0,
32354                 allowfullscreen : true
32355             });
32356         }
32357         
32358         return cfg;
32359         
32360     },
32361     
32362     getSplitAutoCreate : function()
32363     {
32364         var cls = 'masonry-brick masonry-brick-split';
32365         
32366         if(this.href.length){
32367             cls += ' masonry-brick-link';
32368         }
32369         
32370         if(this.bgimage.length){
32371             cls += ' masonry-brick-image';
32372         }
32373         
32374         if(this.size){
32375             cls += ' masonry-' + this.size + '-brick';
32376         }
32377         
32378         switch (this.placetitle) {
32379             case 'center' :
32380                 cls += ' masonry-center-title';
32381                 break;
32382             case 'bottom' :
32383                 cls += ' masonry-bottom-title';
32384                 break;
32385             default:
32386                 if(!this.bgimage.length){
32387                     cls += ' masonry-center-title';
32388                 }
32389
32390                 if(this.bgimage.length){
32391                     cls += ' masonry-bottom-title';
32392                 }
32393                 break;
32394         }
32395         
32396         if(this.cls){
32397             cls += ' ' + this.cls;
32398         }
32399         
32400         var cfg = {
32401             tag: (this.href.length) ? 'a' : 'div',
32402             cls: cls,
32403             cn: [
32404                 {
32405                     tag: 'div',
32406                     cls: 'masonry-brick-split-head',
32407                     cn: [
32408                         {
32409                             tag: 'div',
32410                             cls: 'masonry-brick-paragraph',
32411                             cn: []
32412                         }
32413                     ]
32414                 },
32415                 {
32416                     tag: 'div',
32417                     cls: 'masonry-brick-split-body',
32418                     cn: []
32419                 }
32420             ]
32421         };
32422         
32423         if(this.href.length){
32424             cfg.href = this.href;
32425         }
32426         
32427         if(this.title.length){
32428             cfg.cn[0].cn[0].cn.push({
32429                 tag: 'h4',
32430                 cls: 'masonry-brick-title',
32431                 html: this.title
32432             });
32433         }
32434         
32435         if(this.html.length){
32436             cfg.cn[1].cn.push({
32437                 tag: 'p',
32438                 cls: 'masonry-brick-text',
32439                 html: this.html
32440             });
32441         }
32442
32443         if(this.bgimage.length){
32444             cfg.cn[0].cn.push({
32445                 tag: 'img',
32446                 cls: 'masonry-brick-image-view',
32447                 src: this.bgimage
32448             });
32449         }
32450         
32451         if(this.videourl.length){
32452             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32453             // youtube support only?
32454             cfg.cn[0].cn.cn.push({
32455                 tag: 'iframe',
32456                 cls: 'masonry-brick-image-view',
32457                 src: vurl,
32458                 frameborder : 0,
32459                 allowfullscreen : true
32460             });
32461         }
32462         
32463         return cfg;
32464     },
32465     
32466     initEvents: function() 
32467     {
32468         switch (this.size) {
32469             case 'xs' :
32470                 this.x = 1;
32471                 this.y = 1;
32472                 break;
32473             case 'sm' :
32474                 this.x = 2;
32475                 this.y = 2;
32476                 break;
32477             case 'md' :
32478             case 'md-left' :
32479             case 'md-right' :
32480                 this.x = 3;
32481                 this.y = 3;
32482                 break;
32483             case 'tall' :
32484                 this.x = 2;
32485                 this.y = 3;
32486                 break;
32487             case 'wide' :
32488                 this.x = 3;
32489                 this.y = 2;
32490                 break;
32491             case 'wide-thin' :
32492                 this.x = 3;
32493                 this.y = 1;
32494                 break;
32495                         
32496             default :
32497                 break;
32498         }
32499         
32500         if(Roo.isTouch){
32501             this.el.on('touchstart', this.onTouchStart, this);
32502             this.el.on('touchmove', this.onTouchMove, this);
32503             this.el.on('touchend', this.onTouchEnd, this);
32504             this.el.on('contextmenu', this.onContextMenu, this);
32505         } else {
32506             this.el.on('mouseenter'  ,this.enter, this);
32507             this.el.on('mouseleave', this.leave, this);
32508             this.el.on('click', this.onClick, this);
32509         }
32510         
32511         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32512             this.parent().bricks.push(this);   
32513         }
32514         
32515     },
32516     
32517     onClick: function(e, el)
32518     {
32519         var time = this.endTimer - this.startTimer;
32520         // Roo.log(e.preventDefault());
32521         if(Roo.isTouch){
32522             if(time > 1000){
32523                 e.preventDefault();
32524                 return;
32525             }
32526         }
32527         
32528         if(!this.preventDefault){
32529             return;
32530         }
32531         
32532         e.preventDefault();
32533         
32534         if (this.activcClass != '') {
32535             this.selectBrick();
32536         }
32537         
32538         this.fireEvent('click', this);
32539     },
32540     
32541     enter: function(e, el)
32542     {
32543         e.preventDefault();
32544         
32545         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32546             return;
32547         }
32548         
32549         if(this.bgimage.length && this.html.length){
32550             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32551         }
32552     },
32553     
32554     leave: function(e, el)
32555     {
32556         e.preventDefault();
32557         
32558         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32559             return;
32560         }
32561         
32562         if(this.bgimage.length && this.html.length){
32563             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32564         }
32565     },
32566     
32567     onTouchStart: function(e, el)
32568     {
32569 //        e.preventDefault();
32570         
32571         this.touchmoved = false;
32572         
32573         if(!this.isFitContainer){
32574             return;
32575         }
32576         
32577         if(!this.bgimage.length || !this.html.length){
32578             return;
32579         }
32580         
32581         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32582         
32583         this.timer = new Date().getTime();
32584         
32585     },
32586     
32587     onTouchMove: function(e, el)
32588     {
32589         this.touchmoved = true;
32590     },
32591     
32592     onContextMenu : function(e,el)
32593     {
32594         e.preventDefault();
32595         e.stopPropagation();
32596         return false;
32597     },
32598     
32599     onTouchEnd: function(e, el)
32600     {
32601 //        e.preventDefault();
32602         
32603         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32604         
32605             this.leave(e,el);
32606             
32607             return;
32608         }
32609         
32610         if(!this.bgimage.length || !this.html.length){
32611             
32612             if(this.href.length){
32613                 window.location.href = this.href;
32614             }
32615             
32616             return;
32617         }
32618         
32619         if(!this.isFitContainer){
32620             return;
32621         }
32622         
32623         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32624         
32625         window.location.href = this.href;
32626     },
32627     
32628     //selection on single brick only
32629     selectBrick : function() {
32630         
32631         if (!this.parentId) {
32632             return;
32633         }
32634         
32635         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32636         var index = m.selectedBrick.indexOf(this.id);
32637         
32638         if ( index > -1) {
32639             m.selectedBrick.splice(index,1);
32640             this.el.removeClass(this.activeClass);
32641             return;
32642         }
32643         
32644         for(var i = 0; i < m.selectedBrick.length; i++) {
32645             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32646             b.el.removeClass(b.activeClass);
32647         }
32648         
32649         m.selectedBrick = [];
32650         
32651         m.selectedBrick.push(this.id);
32652         this.el.addClass(this.activeClass);
32653         return;
32654     }
32655     
32656 });
32657
32658 Roo.apply(Roo.bootstrap.MasonryBrick, {
32659     
32660     //groups: {},
32661     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32662      /**
32663     * register a Masonry Brick
32664     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32665     */
32666     
32667     register : function(brick)
32668     {
32669         //this.groups[brick.id] = brick;
32670         this.groups.add(brick.id, brick);
32671     },
32672     /**
32673     * fetch a  masonry brick based on the masonry brick ID
32674     * @param {string} the masonry brick to add
32675     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32676     */
32677     
32678     get: function(brick_id) 
32679     {
32680         // if (typeof(this.groups[brick_id]) == 'undefined') {
32681         //     return false;
32682         // }
32683         // return this.groups[brick_id] ;
32684         
32685         if(this.groups.key(brick_id)) {
32686             return this.groups.key(brick_id);
32687         }
32688         
32689         return false;
32690     }
32691     
32692     
32693     
32694 });
32695
32696  /*
32697  * - LGPL
32698  *
32699  * element
32700  * 
32701  */
32702
32703 /**
32704  * @class Roo.bootstrap.Brick
32705  * @extends Roo.bootstrap.Component
32706  * Bootstrap Brick class
32707  * 
32708  * @constructor
32709  * Create a new Brick
32710  * @param {Object} config The config object
32711  */
32712
32713 Roo.bootstrap.Brick = function(config){
32714     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32715     
32716     this.addEvents({
32717         // raw events
32718         /**
32719          * @event click
32720          * When a Brick is click
32721          * @param {Roo.bootstrap.Brick} this
32722          * @param {Roo.EventObject} e
32723          */
32724         "click" : true
32725     });
32726 };
32727
32728 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32729     
32730     /**
32731      * @cfg {String} title
32732      */   
32733     title : '',
32734     /**
32735      * @cfg {String} html
32736      */   
32737     html : '',
32738     /**
32739      * @cfg {String} bgimage
32740      */   
32741     bgimage : '',
32742     /**
32743      * @cfg {String} cls
32744      */   
32745     cls : '',
32746     /**
32747      * @cfg {String} href
32748      */   
32749     href : '',
32750     /**
32751      * @cfg {String} video
32752      */   
32753     video : '',
32754     /**
32755      * @cfg {Boolean} square
32756      */   
32757     square : true,
32758     
32759     getAutoCreate : function()
32760     {
32761         var cls = 'roo-brick';
32762         
32763         if(this.href.length){
32764             cls += ' roo-brick-link';
32765         }
32766         
32767         if(this.bgimage.length){
32768             cls += ' roo-brick-image';
32769         }
32770         
32771         if(!this.html.length && !this.bgimage.length){
32772             cls += ' roo-brick-center-title';
32773         }
32774         
32775         if(!this.html.length && this.bgimage.length){
32776             cls += ' roo-brick-bottom-title';
32777         }
32778         
32779         if(this.cls){
32780             cls += ' ' + this.cls;
32781         }
32782         
32783         var cfg = {
32784             tag: (this.href.length) ? 'a' : 'div',
32785             cls: cls,
32786             cn: [
32787                 {
32788                     tag: 'div',
32789                     cls: 'roo-brick-paragraph',
32790                     cn: []
32791                 }
32792             ]
32793         };
32794         
32795         if(this.href.length){
32796             cfg.href = this.href;
32797         }
32798         
32799         var cn = cfg.cn[0].cn;
32800         
32801         if(this.title.length){
32802             cn.push({
32803                 tag: 'h4',
32804                 cls: 'roo-brick-title',
32805                 html: this.title
32806             });
32807         }
32808         
32809         if(this.html.length){
32810             cn.push({
32811                 tag: 'p',
32812                 cls: 'roo-brick-text',
32813                 html: this.html
32814             });
32815         } else {
32816             cn.cls += ' hide';
32817         }
32818         
32819         if(this.bgimage.length){
32820             cfg.cn.push({
32821                 tag: 'img',
32822                 cls: 'roo-brick-image-view',
32823                 src: this.bgimage
32824             });
32825         }
32826         
32827         return cfg;
32828     },
32829     
32830     initEvents: function() 
32831     {
32832         if(this.title.length || this.html.length){
32833             this.el.on('mouseenter'  ,this.enter, this);
32834             this.el.on('mouseleave', this.leave, this);
32835         }
32836         
32837         Roo.EventManager.onWindowResize(this.resize, this); 
32838         
32839         if(this.bgimage.length){
32840             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32841             this.imageEl.on('load', this.onImageLoad, this);
32842             return;
32843         }
32844         
32845         this.resize();
32846     },
32847     
32848     onImageLoad : function()
32849     {
32850         this.resize();
32851     },
32852     
32853     resize : function()
32854     {
32855         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32856         
32857         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32858         
32859         if(this.bgimage.length){
32860             var image = this.el.select('.roo-brick-image-view', true).first();
32861             
32862             image.setWidth(paragraph.getWidth());
32863             
32864             if(this.square){
32865                 image.setHeight(paragraph.getWidth());
32866             }
32867             
32868             this.el.setHeight(image.getHeight());
32869             paragraph.setHeight(image.getHeight());
32870             
32871         }
32872         
32873     },
32874     
32875     enter: function(e, el)
32876     {
32877         e.preventDefault();
32878         
32879         if(this.bgimage.length){
32880             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32881             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32882         }
32883     },
32884     
32885     leave: function(e, el)
32886     {
32887         e.preventDefault();
32888         
32889         if(this.bgimage.length){
32890             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32891             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32892         }
32893     }
32894     
32895 });
32896
32897  
32898
32899  /*
32900  * - LGPL
32901  *
32902  * Input
32903  * 
32904  */
32905
32906 /**
32907  * @class Roo.bootstrap.NumberField
32908  * @extends Roo.bootstrap.Input
32909  * Bootstrap NumberField class
32910  * 
32911  * 
32912  * 
32913  * 
32914  * @constructor
32915  * Create a new NumberField
32916  * @param {Object} config The config object
32917  */
32918
32919 Roo.bootstrap.NumberField = function(config){
32920     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32921 };
32922
32923 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32924     
32925     /**
32926      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32927      */
32928     allowDecimals : true,
32929     /**
32930      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32931      */
32932     decimalSeparator : ".",
32933     /**
32934      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32935      */
32936     decimalPrecision : 2,
32937     /**
32938      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32939      */
32940     allowNegative : true,
32941     /**
32942      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32943      */
32944     minValue : Number.NEGATIVE_INFINITY,
32945     /**
32946      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32947      */
32948     maxValue : Number.MAX_VALUE,
32949     /**
32950      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32951      */
32952     minText : "The minimum value for this field is {0}",
32953     /**
32954      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32955      */
32956     maxText : "The maximum value for this field is {0}",
32957     /**
32958      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32959      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32960      */
32961     nanText : "{0} is not a valid number",
32962     /**
32963      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32964      */
32965     castInt : true,
32966
32967     // private
32968     initEvents : function()
32969     {   
32970         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32971         
32972         var allowed = "0123456789";
32973         
32974         if(this.allowDecimals){
32975             allowed += this.decimalSeparator;
32976         }
32977         
32978         if(this.allowNegative){
32979             allowed += "-";
32980         }
32981         
32982         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32983         
32984         var keyPress = function(e){
32985             
32986             var k = e.getKey();
32987             
32988             var c = e.getCharCode();
32989             
32990             if(
32991                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32992                     allowed.indexOf(String.fromCharCode(c)) === -1
32993             ){
32994                 e.stopEvent();
32995                 return;
32996             }
32997             
32998             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32999                 return;
33000             }
33001             
33002             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33003                 e.stopEvent();
33004             }
33005         };
33006         
33007         this.el.on("keypress", keyPress, this);
33008     },
33009     
33010     validateValue : function(value)
33011     {
33012         
33013         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33014             return false;
33015         }
33016         
33017         var num = this.parseValue(value);
33018         
33019         if(isNaN(num)){
33020             this.markInvalid(String.format(this.nanText, value));
33021             return false;
33022         }
33023         
33024         if(num < this.minValue){
33025             this.markInvalid(String.format(this.minText, this.minValue));
33026             return false;
33027         }
33028         
33029         if(num > this.maxValue){
33030             this.markInvalid(String.format(this.maxText, this.maxValue));
33031             return false;
33032         }
33033         
33034         return true;
33035     },
33036
33037     getValue : function()
33038     {
33039         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33040     },
33041
33042     parseValue : function(value)
33043     {
33044         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33045         return isNaN(value) ? '' : value;
33046     },
33047
33048     fixPrecision : function(value)
33049     {
33050         var nan = isNaN(value);
33051         
33052         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33053             return nan ? '' : value;
33054         }
33055         return parseFloat(value).toFixed(this.decimalPrecision);
33056     },
33057
33058     setValue : function(v)
33059     {
33060         v = this.fixPrecision(v);
33061         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33062     },
33063
33064     decimalPrecisionFcn : function(v)
33065     {
33066         return Math.floor(v);
33067     },
33068
33069     beforeBlur : function()
33070     {
33071         if(!this.castInt){
33072             return;
33073         }
33074         
33075         var v = this.parseValue(this.getRawValue());
33076         if(v){
33077             this.setValue(v);
33078         }
33079     }
33080     
33081 });
33082
33083  
33084
33085 /*
33086 * Licence: LGPL
33087 */
33088
33089 /**
33090  * @class Roo.bootstrap.DocumentSlider
33091  * @extends Roo.bootstrap.Component
33092  * Bootstrap DocumentSlider class
33093  * 
33094  * @constructor
33095  * Create a new DocumentViewer
33096  * @param {Object} config The config object
33097  */
33098
33099 Roo.bootstrap.DocumentSlider = function(config){
33100     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33101     
33102     this.files = [];
33103     
33104     this.addEvents({
33105         /**
33106          * @event initial
33107          * Fire after initEvent
33108          * @param {Roo.bootstrap.DocumentSlider} this
33109          */
33110         "initial" : true,
33111         /**
33112          * @event update
33113          * Fire after update
33114          * @param {Roo.bootstrap.DocumentSlider} this
33115          */
33116         "update" : true,
33117         /**
33118          * @event click
33119          * Fire after click
33120          * @param {Roo.bootstrap.DocumentSlider} this
33121          */
33122         "click" : true
33123     });
33124 };
33125
33126 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33127     
33128     files : false,
33129     
33130     indicator : 0,
33131     
33132     getAutoCreate : function()
33133     {
33134         var cfg = {
33135             tag : 'div',
33136             cls : 'roo-document-slider',
33137             cn : [
33138                 {
33139                     tag : 'div',
33140                     cls : 'roo-document-slider-header',
33141                     cn : [
33142                         {
33143                             tag : 'div',
33144                             cls : 'roo-document-slider-header-title'
33145                         }
33146                     ]
33147                 },
33148                 {
33149                     tag : 'div',
33150                     cls : 'roo-document-slider-body',
33151                     cn : [
33152                         {
33153                             tag : 'div',
33154                             cls : 'roo-document-slider-prev',
33155                             cn : [
33156                                 {
33157                                     tag : 'i',
33158                                     cls : 'fa fa-chevron-left'
33159                                 }
33160                             ]
33161                         },
33162                         {
33163                             tag : 'div',
33164                             cls : 'roo-document-slider-thumb',
33165                             cn : [
33166                                 {
33167                                     tag : 'img',
33168                                     cls : 'roo-document-slider-image'
33169                                 }
33170                             ]
33171                         },
33172                         {
33173                             tag : 'div',
33174                             cls : 'roo-document-slider-next',
33175                             cn : [
33176                                 {
33177                                     tag : 'i',
33178                                     cls : 'fa fa-chevron-right'
33179                                 }
33180                             ]
33181                         }
33182                     ]
33183                 }
33184             ]
33185         };
33186         
33187         return cfg;
33188     },
33189     
33190     initEvents : function()
33191     {
33192         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33193         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33194         
33195         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33196         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33197         
33198         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33199         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33200         
33201         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33202         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33203         
33204         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33205         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33206         
33207         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33208         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33209         
33210         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33211         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33212         
33213         this.thumbEl.on('click', this.onClick, this);
33214         
33215         this.prevIndicator.on('click', this.prev, this);
33216         
33217         this.nextIndicator.on('click', this.next, this);
33218         
33219     },
33220     
33221     initial : function()
33222     {
33223         if(this.files.length){
33224             this.indicator = 1;
33225             this.update()
33226         }
33227         
33228         this.fireEvent('initial', this);
33229     },
33230     
33231     update : function()
33232     {
33233         this.imageEl.attr('src', this.files[this.indicator - 1]);
33234         
33235         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33236         
33237         this.prevIndicator.show();
33238         
33239         if(this.indicator == 1){
33240             this.prevIndicator.hide();
33241         }
33242         
33243         this.nextIndicator.show();
33244         
33245         if(this.indicator == this.files.length){
33246             this.nextIndicator.hide();
33247         }
33248         
33249         this.thumbEl.scrollTo('top');
33250         
33251         this.fireEvent('update', this);
33252     },
33253     
33254     onClick : function(e)
33255     {
33256         e.preventDefault();
33257         
33258         this.fireEvent('click', this);
33259     },
33260     
33261     prev : function(e)
33262     {
33263         e.preventDefault();
33264         
33265         this.indicator = Math.max(1, this.indicator - 1);
33266         
33267         this.update();
33268     },
33269     
33270     next : function(e)
33271     {
33272         e.preventDefault();
33273         
33274         this.indicator = Math.min(this.files.length, this.indicator + 1);
33275         
33276         this.update();
33277     }
33278 });
33279 /*
33280  * - LGPL
33281  *
33282  * RadioSet
33283  *
33284  *
33285  */
33286
33287 /**
33288  * @class Roo.bootstrap.RadioSet
33289  * @extends Roo.bootstrap.Input
33290  * Bootstrap RadioSet class
33291  * @cfg {String} indicatorpos (left|right) default left
33292  * @cfg {Boolean} inline (true|false) inline the element (default true)
33293  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33294  * @constructor
33295  * Create a new RadioSet
33296  * @param {Object} config The config object
33297  */
33298
33299 Roo.bootstrap.RadioSet = function(config){
33300     
33301     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33302     
33303     this.radioes = [];
33304     
33305     Roo.bootstrap.RadioSet.register(this);
33306     
33307     this.addEvents({
33308         /**
33309         * @event check
33310         * Fires when the element is checked or unchecked.
33311         * @param {Roo.bootstrap.RadioSet} this This radio
33312         * @param {Roo.bootstrap.Radio} item The checked item
33313         */
33314        check : true
33315     });
33316     
33317 };
33318
33319 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33320
33321     radioes : false,
33322     
33323     inline : true,
33324     
33325     weight : '',
33326     
33327     indicatorpos : 'left',
33328     
33329     getAutoCreate : function()
33330     {
33331         var label = {
33332             tag : 'label',
33333             cls : 'roo-radio-set-label',
33334             cn : [
33335                 {
33336                     tag : 'span',
33337                     html : this.fieldLabel
33338                 }
33339             ]
33340         };
33341         
33342         if(this.indicatorpos == 'left'){
33343             label.cn.unshift({
33344                 tag : 'i',
33345                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33346                 tooltip : 'This field is required'
33347             });
33348         } else {
33349             label.cn.push({
33350                 tag : 'i',
33351                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33352                 tooltip : 'This field is required'
33353             });
33354         }
33355         
33356         var items = {
33357             tag : 'div',
33358             cls : 'roo-radio-set-items'
33359         };
33360         
33361         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33362         
33363         if (align === 'left' && this.fieldLabel.length) {
33364             
33365             items = {
33366                 cls : "roo-radio-set-right", 
33367                 cn: [
33368                     items
33369                 ]
33370             };
33371             
33372             if(this.labelWidth > 12){
33373                 label.style = "width: " + this.labelWidth + 'px';
33374             }
33375             
33376             if(this.labelWidth < 13 && this.labelmd == 0){
33377                 this.labelmd = this.labelWidth;
33378             }
33379             
33380             if(this.labellg > 0){
33381                 label.cls += ' col-lg-' + this.labellg;
33382                 items.cls += ' col-lg-' + (12 - this.labellg);
33383             }
33384             
33385             if(this.labelmd > 0){
33386                 label.cls += ' col-md-' + this.labelmd;
33387                 items.cls += ' col-md-' + (12 - this.labelmd);
33388             }
33389             
33390             if(this.labelsm > 0){
33391                 label.cls += ' col-sm-' + this.labelsm;
33392                 items.cls += ' col-sm-' + (12 - this.labelsm);
33393             }
33394             
33395             if(this.labelxs > 0){
33396                 label.cls += ' col-xs-' + this.labelxs;
33397                 items.cls += ' col-xs-' + (12 - this.labelxs);
33398             }
33399         }
33400         
33401         var cfg = {
33402             tag : 'div',
33403             cls : 'roo-radio-set',
33404             cn : [
33405                 {
33406                     tag : 'input',
33407                     cls : 'roo-radio-set-input',
33408                     type : 'hidden',
33409                     name : this.name,
33410                     value : this.value ? this.value :  ''
33411                 },
33412                 label,
33413                 items
33414             ]
33415         };
33416         
33417         if(this.weight.length){
33418             cfg.cls += ' roo-radio-' + this.weight;
33419         }
33420         
33421         if(this.inline) {
33422             cfg.cls += ' roo-radio-set-inline';
33423         }
33424         
33425         var settings=this;
33426         ['xs','sm','md','lg'].map(function(size){
33427             if (settings[size]) {
33428                 cfg.cls += ' col-' + size + '-' + settings[size];
33429             }
33430         });
33431         
33432         return cfg;
33433         
33434     },
33435
33436     initEvents : function()
33437     {
33438         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33439         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33440         
33441         if(!this.fieldLabel.length){
33442             this.labelEl.hide();
33443         }
33444         
33445         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33446         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33447         
33448         this.indicatorEl().addClass('invisible');
33449         
33450         this.originalValue = this.getValue();
33451         
33452     },
33453     
33454     inputEl: function ()
33455     {
33456         return this.el.select('.roo-radio-set-input', true).first();
33457     },
33458     
33459     getChildContainer : function()
33460     {
33461         return this.itemsEl;
33462     },
33463     
33464     register : function(item)
33465     {
33466         this.radioes.push(item);
33467         
33468     },
33469     
33470     validate : function()
33471     {   
33472         var valid = false;
33473         
33474         Roo.each(this.radioes, function(i){
33475             if(!i.checked){
33476                 return;
33477             }
33478             
33479             valid = true;
33480             return false;
33481         });
33482         
33483         if(this.allowBlank) {
33484             return true;
33485         }
33486         
33487         if(this.disabled || valid){
33488             this.markValid();
33489             return true;
33490         }
33491         
33492         this.markInvalid();
33493         return false;
33494         
33495     },
33496     
33497     markValid : function()
33498     {
33499         if(this.labelEl.isVisible(true)){
33500             this.indicatorEl().removeClass('visible');
33501             this.indicatorEl().addClass('invisible');
33502         }
33503         
33504         this.el.removeClass([this.invalidClass, this.validClass]);
33505         this.el.addClass(this.validClass);
33506         
33507         this.fireEvent('valid', this);
33508     },
33509     
33510     markInvalid : function(msg)
33511     {
33512         if(this.allowBlank || this.disabled){
33513             return;
33514         }
33515         
33516         if(this.labelEl.isVisible(true)){
33517             this.indicatorEl().removeClass('invisible');
33518             this.indicatorEl().addClass('visible');
33519         }
33520         
33521         this.el.removeClass([this.invalidClass, this.validClass]);
33522         this.el.addClass(this.invalidClass);
33523         
33524         this.fireEvent('invalid', this, msg);
33525         
33526     },
33527     
33528     setValue : function(v, suppressEvent)
33529     {   
33530         if(this.value === v){
33531             return;
33532         }
33533         
33534         this.value = v;
33535         
33536         if(this.rendered){
33537             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33538         }
33539         
33540         Roo.each(this.radioes, function(i){
33541             
33542             i.checked = false;
33543             i.el.removeClass('checked');
33544             
33545             if(i.value === v || i.value.toString() === v.toString()){
33546                 i.checked = true;
33547                 i.el.addClass('checked');
33548                 
33549                 if(suppressEvent !== true){
33550                     this.fireEvent('check', this, i);
33551                 }
33552             }
33553             
33554         }, this);
33555         
33556         this.validate();
33557     },
33558     
33559     clearInvalid : function(){
33560         
33561         if(!this.el || this.preventMark){
33562             return;
33563         }
33564         
33565         this.el.removeClass([this.invalidClass]);
33566         
33567         this.fireEvent('valid', this);
33568     }
33569     
33570 });
33571
33572 Roo.apply(Roo.bootstrap.RadioSet, {
33573     
33574     groups: {},
33575     
33576     register : function(set)
33577     {
33578         this.groups[set.name] = set;
33579     },
33580     
33581     get: function(name) 
33582     {
33583         if (typeof(this.groups[name]) == 'undefined') {
33584             return false;
33585         }
33586         
33587         return this.groups[name] ;
33588     }
33589     
33590 });
33591 /*
33592  * Based on:
33593  * Ext JS Library 1.1.1
33594  * Copyright(c) 2006-2007, Ext JS, LLC.
33595  *
33596  * Originally Released Under LGPL - original licence link has changed is not relivant.
33597  *
33598  * Fork - LGPL
33599  * <script type="text/javascript">
33600  */
33601
33602
33603 /**
33604  * @class Roo.bootstrap.SplitBar
33605  * @extends Roo.util.Observable
33606  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33607  * <br><br>
33608  * Usage:
33609  * <pre><code>
33610 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33611                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33612 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33613 split.minSize = 100;
33614 split.maxSize = 600;
33615 split.animate = true;
33616 split.on('moved', splitterMoved);
33617 </code></pre>
33618  * @constructor
33619  * Create a new SplitBar
33620  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33621  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33622  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33623  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33624                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33625                         position of the SplitBar).
33626  */
33627 Roo.bootstrap.SplitBar = function(cfg){
33628     
33629     /** @private */
33630     
33631     //{
33632     //  dragElement : elm
33633     //  resizingElement: el,
33634         // optional..
33635     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33636     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33637         // existingProxy ???
33638     //}
33639     
33640     this.el = Roo.get(cfg.dragElement, true);
33641     this.el.dom.unselectable = "on";
33642     /** @private */
33643     this.resizingEl = Roo.get(cfg.resizingElement, true);
33644
33645     /**
33646      * @private
33647      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33648      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33649      * @type Number
33650      */
33651     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33652     
33653     /**
33654      * The minimum size of the resizing element. (Defaults to 0)
33655      * @type Number
33656      */
33657     this.minSize = 0;
33658     
33659     /**
33660      * The maximum size of the resizing element. (Defaults to 2000)
33661      * @type Number
33662      */
33663     this.maxSize = 2000;
33664     
33665     /**
33666      * Whether to animate the transition to the new size
33667      * @type Boolean
33668      */
33669     this.animate = false;
33670     
33671     /**
33672      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33673      * @type Boolean
33674      */
33675     this.useShim = false;
33676     
33677     /** @private */
33678     this.shim = null;
33679     
33680     if(!cfg.existingProxy){
33681         /** @private */
33682         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33683     }else{
33684         this.proxy = Roo.get(cfg.existingProxy).dom;
33685     }
33686     /** @private */
33687     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33688     
33689     /** @private */
33690     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33691     
33692     /** @private */
33693     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33694     
33695     /** @private */
33696     this.dragSpecs = {};
33697     
33698     /**
33699      * @private The adapter to use to positon and resize elements
33700      */
33701     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33702     this.adapter.init(this);
33703     
33704     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33705         /** @private */
33706         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33707         this.el.addClass("roo-splitbar-h");
33708     }else{
33709         /** @private */
33710         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33711         this.el.addClass("roo-splitbar-v");
33712     }
33713     
33714     this.addEvents({
33715         /**
33716          * @event resize
33717          * Fires when the splitter is moved (alias for {@link #event-moved})
33718          * @param {Roo.bootstrap.SplitBar} this
33719          * @param {Number} newSize the new width or height
33720          */
33721         "resize" : true,
33722         /**
33723          * @event moved
33724          * Fires when the splitter is moved
33725          * @param {Roo.bootstrap.SplitBar} this
33726          * @param {Number} newSize the new width or height
33727          */
33728         "moved" : true,
33729         /**
33730          * @event beforeresize
33731          * Fires before the splitter is dragged
33732          * @param {Roo.bootstrap.SplitBar} this
33733          */
33734         "beforeresize" : true,
33735
33736         "beforeapply" : true
33737     });
33738
33739     Roo.util.Observable.call(this);
33740 };
33741
33742 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33743     onStartProxyDrag : function(x, y){
33744         this.fireEvent("beforeresize", this);
33745         if(!this.overlay){
33746             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33747             o.unselectable();
33748             o.enableDisplayMode("block");
33749             // all splitbars share the same overlay
33750             Roo.bootstrap.SplitBar.prototype.overlay = o;
33751         }
33752         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33753         this.overlay.show();
33754         Roo.get(this.proxy).setDisplayed("block");
33755         var size = this.adapter.getElementSize(this);
33756         this.activeMinSize = this.getMinimumSize();;
33757         this.activeMaxSize = this.getMaximumSize();;
33758         var c1 = size - this.activeMinSize;
33759         var c2 = Math.max(this.activeMaxSize - size, 0);
33760         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33761             this.dd.resetConstraints();
33762             this.dd.setXConstraint(
33763                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33764                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33765             );
33766             this.dd.setYConstraint(0, 0);
33767         }else{
33768             this.dd.resetConstraints();
33769             this.dd.setXConstraint(0, 0);
33770             this.dd.setYConstraint(
33771                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33772                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33773             );
33774          }
33775         this.dragSpecs.startSize = size;
33776         this.dragSpecs.startPoint = [x, y];
33777         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33778     },
33779     
33780     /** 
33781      * @private Called after the drag operation by the DDProxy
33782      */
33783     onEndProxyDrag : function(e){
33784         Roo.get(this.proxy).setDisplayed(false);
33785         var endPoint = Roo.lib.Event.getXY(e);
33786         if(this.overlay){
33787             this.overlay.hide();
33788         }
33789         var newSize;
33790         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33791             newSize = this.dragSpecs.startSize + 
33792                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33793                     endPoint[0] - this.dragSpecs.startPoint[0] :
33794                     this.dragSpecs.startPoint[0] - endPoint[0]
33795                 );
33796         }else{
33797             newSize = this.dragSpecs.startSize + 
33798                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33799                     endPoint[1] - this.dragSpecs.startPoint[1] :
33800                     this.dragSpecs.startPoint[1] - endPoint[1]
33801                 );
33802         }
33803         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33804         if(newSize != this.dragSpecs.startSize){
33805             if(this.fireEvent('beforeapply', this, newSize) !== false){
33806                 this.adapter.setElementSize(this, newSize);
33807                 this.fireEvent("moved", this, newSize);
33808                 this.fireEvent("resize", this, newSize);
33809             }
33810         }
33811     },
33812     
33813     /**
33814      * Get the adapter this SplitBar uses
33815      * @return The adapter object
33816      */
33817     getAdapter : function(){
33818         return this.adapter;
33819     },
33820     
33821     /**
33822      * Set the adapter this SplitBar uses
33823      * @param {Object} adapter A SplitBar adapter object
33824      */
33825     setAdapter : function(adapter){
33826         this.adapter = adapter;
33827         this.adapter.init(this);
33828     },
33829     
33830     /**
33831      * Gets the minimum size for the resizing element
33832      * @return {Number} The minimum size
33833      */
33834     getMinimumSize : function(){
33835         return this.minSize;
33836     },
33837     
33838     /**
33839      * Sets the minimum size for the resizing element
33840      * @param {Number} minSize The minimum size
33841      */
33842     setMinimumSize : function(minSize){
33843         this.minSize = minSize;
33844     },
33845     
33846     /**
33847      * Gets the maximum size for the resizing element
33848      * @return {Number} The maximum size
33849      */
33850     getMaximumSize : function(){
33851         return this.maxSize;
33852     },
33853     
33854     /**
33855      * Sets the maximum size for the resizing element
33856      * @param {Number} maxSize The maximum size
33857      */
33858     setMaximumSize : function(maxSize){
33859         this.maxSize = maxSize;
33860     },
33861     
33862     /**
33863      * Sets the initialize size for the resizing element
33864      * @param {Number} size The initial size
33865      */
33866     setCurrentSize : function(size){
33867         var oldAnimate = this.animate;
33868         this.animate = false;
33869         this.adapter.setElementSize(this, size);
33870         this.animate = oldAnimate;
33871     },
33872     
33873     /**
33874      * Destroy this splitbar. 
33875      * @param {Boolean} removeEl True to remove the element
33876      */
33877     destroy : function(removeEl){
33878         if(this.shim){
33879             this.shim.remove();
33880         }
33881         this.dd.unreg();
33882         this.proxy.parentNode.removeChild(this.proxy);
33883         if(removeEl){
33884             this.el.remove();
33885         }
33886     }
33887 });
33888
33889 /**
33890  * @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.
33891  */
33892 Roo.bootstrap.SplitBar.createProxy = function(dir){
33893     var proxy = new Roo.Element(document.createElement("div"));
33894     proxy.unselectable();
33895     var cls = 'roo-splitbar-proxy';
33896     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33897     document.body.appendChild(proxy.dom);
33898     return proxy.dom;
33899 };
33900
33901 /** 
33902  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33903  * Default Adapter. It assumes the splitter and resizing element are not positioned
33904  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33905  */
33906 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33907 };
33908
33909 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33910     // do nothing for now
33911     init : function(s){
33912     
33913     },
33914     /**
33915      * Called before drag operations to get the current size of the resizing element. 
33916      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33917      */
33918      getElementSize : function(s){
33919         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33920             return s.resizingEl.getWidth();
33921         }else{
33922             return s.resizingEl.getHeight();
33923         }
33924     },
33925     
33926     /**
33927      * Called after drag operations to set the size of the resizing element.
33928      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33929      * @param {Number} newSize The new size to set
33930      * @param {Function} onComplete A function to be invoked when resizing is complete
33931      */
33932     setElementSize : function(s, newSize, onComplete){
33933         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33934             if(!s.animate){
33935                 s.resizingEl.setWidth(newSize);
33936                 if(onComplete){
33937                     onComplete(s, newSize);
33938                 }
33939             }else{
33940                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33941             }
33942         }else{
33943             
33944             if(!s.animate){
33945                 s.resizingEl.setHeight(newSize);
33946                 if(onComplete){
33947                     onComplete(s, newSize);
33948                 }
33949             }else{
33950                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33951             }
33952         }
33953     }
33954 };
33955
33956 /** 
33957  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33958  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33959  * Adapter that  moves the splitter element to align with the resized sizing element. 
33960  * Used with an absolute positioned SplitBar.
33961  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33962  * document.body, make sure you assign an id to the body element.
33963  */
33964 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33965     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33966     this.container = Roo.get(container);
33967 };
33968
33969 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33970     init : function(s){
33971         this.basic.init(s);
33972     },
33973     
33974     getElementSize : function(s){
33975         return this.basic.getElementSize(s);
33976     },
33977     
33978     setElementSize : function(s, newSize, onComplete){
33979         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33980     },
33981     
33982     moveSplitter : function(s){
33983         var yes = Roo.bootstrap.SplitBar;
33984         switch(s.placement){
33985             case yes.LEFT:
33986                 s.el.setX(s.resizingEl.getRight());
33987                 break;
33988             case yes.RIGHT:
33989                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33990                 break;
33991             case yes.TOP:
33992                 s.el.setY(s.resizingEl.getBottom());
33993                 break;
33994             case yes.BOTTOM:
33995                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33996                 break;
33997         }
33998     }
33999 };
34000
34001 /**
34002  * Orientation constant - Create a vertical SplitBar
34003  * @static
34004  * @type Number
34005  */
34006 Roo.bootstrap.SplitBar.VERTICAL = 1;
34007
34008 /**
34009  * Orientation constant - Create a horizontal SplitBar
34010  * @static
34011  * @type Number
34012  */
34013 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34014
34015 /**
34016  * Placement constant - The resizing element is to the left of the splitter element
34017  * @static
34018  * @type Number
34019  */
34020 Roo.bootstrap.SplitBar.LEFT = 1;
34021
34022 /**
34023  * Placement constant - The resizing element is to the right of the splitter element
34024  * @static
34025  * @type Number
34026  */
34027 Roo.bootstrap.SplitBar.RIGHT = 2;
34028
34029 /**
34030  * Placement constant - The resizing element is positioned above the splitter element
34031  * @static
34032  * @type Number
34033  */
34034 Roo.bootstrap.SplitBar.TOP = 3;
34035
34036 /**
34037  * Placement constant - The resizing element is positioned under splitter element
34038  * @static
34039  * @type Number
34040  */
34041 Roo.bootstrap.SplitBar.BOTTOM = 4;
34042 Roo.namespace("Roo.bootstrap.layout");/*
34043  * Based on:
34044  * Ext JS Library 1.1.1
34045  * Copyright(c) 2006-2007, Ext JS, LLC.
34046  *
34047  * Originally Released Under LGPL - original licence link has changed is not relivant.
34048  *
34049  * Fork - LGPL
34050  * <script type="text/javascript">
34051  */
34052
34053 /**
34054  * @class Roo.bootstrap.layout.Manager
34055  * @extends Roo.bootstrap.Component
34056  * Base class for layout managers.
34057  */
34058 Roo.bootstrap.layout.Manager = function(config)
34059 {
34060     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34061
34062
34063
34064
34065
34066     /** false to disable window resize monitoring @type Boolean */
34067     this.monitorWindowResize = true;
34068     this.regions = {};
34069     this.addEvents({
34070         /**
34071          * @event layout
34072          * Fires when a layout is performed.
34073          * @param {Roo.LayoutManager} this
34074          */
34075         "layout" : true,
34076         /**
34077          * @event regionresized
34078          * Fires when the user resizes a region.
34079          * @param {Roo.LayoutRegion} region The resized region
34080          * @param {Number} newSize The new size (width for east/west, height for north/south)
34081          */
34082         "regionresized" : true,
34083         /**
34084          * @event regioncollapsed
34085          * Fires when a region is collapsed.
34086          * @param {Roo.LayoutRegion} region The collapsed region
34087          */
34088         "regioncollapsed" : true,
34089         /**
34090          * @event regionexpanded
34091          * Fires when a region is expanded.
34092          * @param {Roo.LayoutRegion} region The expanded region
34093          */
34094         "regionexpanded" : true
34095     });
34096     this.updating = false;
34097
34098     if (config.el) {
34099         this.el = Roo.get(config.el);
34100         this.initEvents();
34101     }
34102
34103 };
34104
34105 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34106
34107
34108     regions : null,
34109
34110     monitorWindowResize : true,
34111
34112
34113     updating : false,
34114
34115
34116     onRender : function(ct, position)
34117     {
34118         if(!this.el){
34119             this.el = Roo.get(ct);
34120             this.initEvents();
34121         }
34122         //this.fireEvent('render',this);
34123     },
34124
34125
34126     initEvents: function()
34127     {
34128
34129
34130         // ie scrollbar fix
34131         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34132             document.body.scroll = "no";
34133         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34134             this.el.position('relative');
34135         }
34136         this.id = this.el.id;
34137         this.el.addClass("roo-layout-container");
34138         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34139         if(this.el.dom != document.body ) {
34140             this.el.on('resize', this.layout,this);
34141             this.el.on('show', this.layout,this);
34142         }
34143
34144     },
34145
34146     /**
34147      * Returns true if this layout is currently being updated
34148      * @return {Boolean}
34149      */
34150     isUpdating : function(){
34151         return this.updating;
34152     },
34153
34154     /**
34155      * Suspend the LayoutManager from doing auto-layouts while
34156      * making multiple add or remove calls
34157      */
34158     beginUpdate : function(){
34159         this.updating = true;
34160     },
34161
34162     /**
34163      * Restore auto-layouts and optionally disable the manager from performing a layout
34164      * @param {Boolean} noLayout true to disable a layout update
34165      */
34166     endUpdate : function(noLayout){
34167         this.updating = false;
34168         if(!noLayout){
34169             this.layout();
34170         }
34171     },
34172
34173     layout: function(){
34174         // abstract...
34175     },
34176
34177     onRegionResized : function(region, newSize){
34178         this.fireEvent("regionresized", region, newSize);
34179         this.layout();
34180     },
34181
34182     onRegionCollapsed : function(region){
34183         this.fireEvent("regioncollapsed", region);
34184     },
34185
34186     onRegionExpanded : function(region){
34187         this.fireEvent("regionexpanded", region);
34188     },
34189
34190     /**
34191      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34192      * performs box-model adjustments.
34193      * @return {Object} The size as an object {width: (the width), height: (the height)}
34194      */
34195     getViewSize : function()
34196     {
34197         var size;
34198         if(this.el.dom != document.body){
34199             size = this.el.getSize();
34200         }else{
34201             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34202         }
34203         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34204         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34205         return size;
34206     },
34207
34208     /**
34209      * Returns the Element this layout is bound to.
34210      * @return {Roo.Element}
34211      */
34212     getEl : function(){
34213         return this.el;
34214     },
34215
34216     /**
34217      * Returns the specified region.
34218      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34219      * @return {Roo.LayoutRegion}
34220      */
34221     getRegion : function(target){
34222         return this.regions[target.toLowerCase()];
34223     },
34224
34225     onWindowResize : function(){
34226         if(this.monitorWindowResize){
34227             this.layout();
34228         }
34229     }
34230 });
34231 /*
34232  * Based on:
34233  * Ext JS Library 1.1.1
34234  * Copyright(c) 2006-2007, Ext JS, LLC.
34235  *
34236  * Originally Released Under LGPL - original licence link has changed is not relivant.
34237  *
34238  * Fork - LGPL
34239  * <script type="text/javascript">
34240  */
34241 /**
34242  * @class Roo.bootstrap.layout.Border
34243  * @extends Roo.bootstrap.layout.Manager
34244  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34245  * please see: examples/bootstrap/nested.html<br><br>
34246  
34247 <b>The container the layout is rendered into can be either the body element or any other element.
34248 If it is not the body element, the container needs to either be an absolute positioned element,
34249 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34250 the container size if it is not the body element.</b>
34251
34252 * @constructor
34253 * Create a new Border
34254 * @param {Object} config Configuration options
34255  */
34256 Roo.bootstrap.layout.Border = function(config){
34257     config = config || {};
34258     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34259     
34260     
34261     
34262     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34263         if(config[region]){
34264             config[region].region = region;
34265             this.addRegion(config[region]);
34266         }
34267     },this);
34268     
34269 };
34270
34271 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34272
34273 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34274     /**
34275      * Creates and adds a new region if it doesn't already exist.
34276      * @param {String} target The target region key (north, south, east, west or center).
34277      * @param {Object} config The regions config object
34278      * @return {BorderLayoutRegion} The new region
34279      */
34280     addRegion : function(config)
34281     {
34282         if(!this.regions[config.region]){
34283             var r = this.factory(config);
34284             this.bindRegion(r);
34285         }
34286         return this.regions[config.region];
34287     },
34288
34289     // private (kinda)
34290     bindRegion : function(r){
34291         this.regions[r.config.region] = r;
34292         
34293         r.on("visibilitychange",    this.layout, this);
34294         r.on("paneladded",          this.layout, this);
34295         r.on("panelremoved",        this.layout, this);
34296         r.on("invalidated",         this.layout, this);
34297         r.on("resized",             this.onRegionResized, this);
34298         r.on("collapsed",           this.onRegionCollapsed, this);
34299         r.on("expanded",            this.onRegionExpanded, this);
34300     },
34301
34302     /**
34303      * Performs a layout update.
34304      */
34305     layout : function()
34306     {
34307         if(this.updating) {
34308             return;
34309         }
34310         
34311         // render all the rebions if they have not been done alreayd?
34312         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34313             if(this.regions[region] && !this.regions[region].bodyEl){
34314                 this.regions[region].onRender(this.el)
34315             }
34316         },this);
34317         
34318         var size = this.getViewSize();
34319         var w = size.width;
34320         var h = size.height;
34321         var centerW = w;
34322         var centerH = h;
34323         var centerY = 0;
34324         var centerX = 0;
34325         //var x = 0, y = 0;
34326
34327         var rs = this.regions;
34328         var north = rs["north"];
34329         var south = rs["south"]; 
34330         var west = rs["west"];
34331         var east = rs["east"];
34332         var center = rs["center"];
34333         //if(this.hideOnLayout){ // not supported anymore
34334             //c.el.setStyle("display", "none");
34335         //}
34336         if(north && north.isVisible()){
34337             var b = north.getBox();
34338             var m = north.getMargins();
34339             b.width = w - (m.left+m.right);
34340             b.x = m.left;
34341             b.y = m.top;
34342             centerY = b.height + b.y + m.bottom;
34343             centerH -= centerY;
34344             north.updateBox(this.safeBox(b));
34345         }
34346         if(south && south.isVisible()){
34347             var b = south.getBox();
34348             var m = south.getMargins();
34349             b.width = w - (m.left+m.right);
34350             b.x = m.left;
34351             var totalHeight = (b.height + m.top + m.bottom);
34352             b.y = h - totalHeight + m.top;
34353             centerH -= totalHeight;
34354             south.updateBox(this.safeBox(b));
34355         }
34356         if(west && west.isVisible()){
34357             var b = west.getBox();
34358             var m = west.getMargins();
34359             b.height = centerH - (m.top+m.bottom);
34360             b.x = m.left;
34361             b.y = centerY + m.top;
34362             var totalWidth = (b.width + m.left + m.right);
34363             centerX += totalWidth;
34364             centerW -= totalWidth;
34365             west.updateBox(this.safeBox(b));
34366         }
34367         if(east && east.isVisible()){
34368             var b = east.getBox();
34369             var m = east.getMargins();
34370             b.height = centerH - (m.top+m.bottom);
34371             var totalWidth = (b.width + m.left + m.right);
34372             b.x = w - totalWidth + m.left;
34373             b.y = centerY + m.top;
34374             centerW -= totalWidth;
34375             east.updateBox(this.safeBox(b));
34376         }
34377         if(center){
34378             var m = center.getMargins();
34379             var centerBox = {
34380                 x: centerX + m.left,
34381                 y: centerY + m.top,
34382                 width: centerW - (m.left+m.right),
34383                 height: centerH - (m.top+m.bottom)
34384             };
34385             //if(this.hideOnLayout){
34386                 //center.el.setStyle("display", "block");
34387             //}
34388             center.updateBox(this.safeBox(centerBox));
34389         }
34390         this.el.repaint();
34391         this.fireEvent("layout", this);
34392     },
34393
34394     // private
34395     safeBox : function(box){
34396         box.width = Math.max(0, box.width);
34397         box.height = Math.max(0, box.height);
34398         return box;
34399     },
34400
34401     /**
34402      * Adds a ContentPanel (or subclass) to this layout.
34403      * @param {String} target The target region key (north, south, east, west or center).
34404      * @param {Roo.ContentPanel} panel The panel to add
34405      * @return {Roo.ContentPanel} The added panel
34406      */
34407     add : function(target, panel){
34408          
34409         target = target.toLowerCase();
34410         return this.regions[target].add(panel);
34411     },
34412
34413     /**
34414      * Remove a ContentPanel (or subclass) to this layout.
34415      * @param {String} target The target region key (north, south, east, west or center).
34416      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34417      * @return {Roo.ContentPanel} The removed panel
34418      */
34419     remove : function(target, panel){
34420         target = target.toLowerCase();
34421         return this.regions[target].remove(panel);
34422     },
34423
34424     /**
34425      * Searches all regions for a panel with the specified id
34426      * @param {String} panelId
34427      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34428      */
34429     findPanel : function(panelId){
34430         var rs = this.regions;
34431         for(var target in rs){
34432             if(typeof rs[target] != "function"){
34433                 var p = rs[target].getPanel(panelId);
34434                 if(p){
34435                     return p;
34436                 }
34437             }
34438         }
34439         return null;
34440     },
34441
34442     /**
34443      * Searches all regions for a panel with the specified id and activates (shows) it.
34444      * @param {String/ContentPanel} panelId The panels id or the panel itself
34445      * @return {Roo.ContentPanel} The shown panel or null
34446      */
34447     showPanel : function(panelId) {
34448       var rs = this.regions;
34449       for(var target in rs){
34450          var r = rs[target];
34451          if(typeof r != "function"){
34452             if(r.hasPanel(panelId)){
34453                return r.showPanel(panelId);
34454             }
34455          }
34456       }
34457       return null;
34458    },
34459
34460    /**
34461      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34462      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34463      */
34464    /*
34465     restoreState : function(provider){
34466         if(!provider){
34467             provider = Roo.state.Manager;
34468         }
34469         var sm = new Roo.LayoutStateManager();
34470         sm.init(this, provider);
34471     },
34472 */
34473  
34474  
34475     /**
34476      * Adds a xtype elements to the layout.
34477      * <pre><code>
34478
34479 layout.addxtype({
34480        xtype : 'ContentPanel',
34481        region: 'west',
34482        items: [ .... ]
34483    }
34484 );
34485
34486 layout.addxtype({
34487         xtype : 'NestedLayoutPanel',
34488         region: 'west',
34489         layout: {
34490            center: { },
34491            west: { }   
34492         },
34493         items : [ ... list of content panels or nested layout panels.. ]
34494    }
34495 );
34496 </code></pre>
34497      * @param {Object} cfg Xtype definition of item to add.
34498      */
34499     addxtype : function(cfg)
34500     {
34501         // basically accepts a pannel...
34502         // can accept a layout region..!?!?
34503         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34504         
34505         
34506         // theory?  children can only be panels??
34507         
34508         //if (!cfg.xtype.match(/Panel$/)) {
34509         //    return false;
34510         //}
34511         var ret = false;
34512         
34513         if (typeof(cfg.region) == 'undefined') {
34514             Roo.log("Failed to add Panel, region was not set");
34515             Roo.log(cfg);
34516             return false;
34517         }
34518         var region = cfg.region;
34519         delete cfg.region;
34520         
34521           
34522         var xitems = [];
34523         if (cfg.items) {
34524             xitems = cfg.items;
34525             delete cfg.items;
34526         }
34527         var nb = false;
34528         
34529         switch(cfg.xtype) 
34530         {
34531             case 'Content':  // ContentPanel (el, cfg)
34532             case 'Scroll':  // ContentPanel (el, cfg)
34533             case 'View': 
34534                 cfg.autoCreate = true;
34535                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34536                 //} else {
34537                 //    var el = this.el.createChild();
34538                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34539                 //}
34540                 
34541                 this.add(region, ret);
34542                 break;
34543             
34544             /*
34545             case 'TreePanel': // our new panel!
34546                 cfg.el = this.el.createChild();
34547                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34548                 this.add(region, ret);
34549                 break;
34550             */
34551             
34552             case 'Nest': 
34553                 // create a new Layout (which is  a Border Layout...
34554                 
34555                 var clayout = cfg.layout;
34556                 clayout.el  = this.el.createChild();
34557                 clayout.items   = clayout.items  || [];
34558                 
34559                 delete cfg.layout;
34560                 
34561                 // replace this exitems with the clayout ones..
34562                 xitems = clayout.items;
34563                  
34564                 // force background off if it's in center...
34565                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34566                     cfg.background = false;
34567                 }
34568                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34569                 
34570                 
34571                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34572                 //console.log('adding nested layout panel '  + cfg.toSource());
34573                 this.add(region, ret);
34574                 nb = {}; /// find first...
34575                 break;
34576             
34577             case 'Grid':
34578                 
34579                 // needs grid and region
34580                 
34581                 //var el = this.getRegion(region).el.createChild();
34582                 /*
34583                  *var el = this.el.createChild();
34584                 // create the grid first...
34585                 cfg.grid.container = el;
34586                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34587                 */
34588                 
34589                 if (region == 'center' && this.active ) {
34590                     cfg.background = false;
34591                 }
34592                 
34593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34594                 
34595                 this.add(region, ret);
34596                 /*
34597                 if (cfg.background) {
34598                     // render grid on panel activation (if panel background)
34599                     ret.on('activate', function(gp) {
34600                         if (!gp.grid.rendered) {
34601                     //        gp.grid.render(el);
34602                         }
34603                     });
34604                 } else {
34605                   //  cfg.grid.render(el);
34606                 }
34607                 */
34608                 break;
34609            
34610            
34611             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34612                 // it was the old xcomponent building that caused this before.
34613                 // espeically if border is the top element in the tree.
34614                 ret = this;
34615                 break; 
34616                 
34617                     
34618                 
34619                 
34620                 
34621             default:
34622                 /*
34623                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34624                     
34625                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34626                     this.add(region, ret);
34627                 } else {
34628                 */
34629                     Roo.log(cfg);
34630                     throw "Can not add '" + cfg.xtype + "' to Border";
34631                     return null;
34632              
34633                                 
34634              
34635         }
34636         this.beginUpdate();
34637         // add children..
34638         var region = '';
34639         var abn = {};
34640         Roo.each(xitems, function(i)  {
34641             region = nb && i.region ? i.region : false;
34642             
34643             var add = ret.addxtype(i);
34644            
34645             if (region) {
34646                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34647                 if (!i.background) {
34648                     abn[region] = nb[region] ;
34649                 }
34650             }
34651             
34652         });
34653         this.endUpdate();
34654
34655         // make the last non-background panel active..
34656         //if (nb) { Roo.log(abn); }
34657         if (nb) {
34658             
34659             for(var r in abn) {
34660                 region = this.getRegion(r);
34661                 if (region) {
34662                     // tried using nb[r], but it does not work..
34663                      
34664                     region.showPanel(abn[r]);
34665                    
34666                 }
34667             }
34668         }
34669         return ret;
34670         
34671     },
34672     
34673     
34674 // private
34675     factory : function(cfg)
34676     {
34677         
34678         var validRegions = Roo.bootstrap.layout.Border.regions;
34679
34680         var target = cfg.region;
34681         cfg.mgr = this;
34682         
34683         var r = Roo.bootstrap.layout;
34684         Roo.log(target);
34685         switch(target){
34686             case "north":
34687                 return new r.North(cfg);
34688             case "south":
34689                 return new r.South(cfg);
34690             case "east":
34691                 return new r.East(cfg);
34692             case "west":
34693                 return new r.West(cfg);
34694             case "center":
34695                 return new r.Center(cfg);
34696         }
34697         throw 'Layout region "'+target+'" not supported.';
34698     }
34699     
34700     
34701 });
34702  /*
34703  * Based on:
34704  * Ext JS Library 1.1.1
34705  * Copyright(c) 2006-2007, Ext JS, LLC.
34706  *
34707  * Originally Released Under LGPL - original licence link has changed is not relivant.
34708  *
34709  * Fork - LGPL
34710  * <script type="text/javascript">
34711  */
34712  
34713 /**
34714  * @class Roo.bootstrap.layout.Basic
34715  * @extends Roo.util.Observable
34716  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34717  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34718  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34719  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34720  * @cfg {string}   region  the region that it inhabits..
34721  * @cfg {bool}   skipConfig skip config?
34722  * 
34723
34724  */
34725 Roo.bootstrap.layout.Basic = function(config){
34726     
34727     this.mgr = config.mgr;
34728     
34729     this.position = config.region;
34730     
34731     var skipConfig = config.skipConfig;
34732     
34733     this.events = {
34734         /**
34735          * @scope Roo.BasicLayoutRegion
34736          */
34737         
34738         /**
34739          * @event beforeremove
34740          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34741          * @param {Roo.LayoutRegion} this
34742          * @param {Roo.ContentPanel} panel The panel
34743          * @param {Object} e The cancel event object
34744          */
34745         "beforeremove" : true,
34746         /**
34747          * @event invalidated
34748          * Fires when the layout for this region is changed.
34749          * @param {Roo.LayoutRegion} this
34750          */
34751         "invalidated" : true,
34752         /**
34753          * @event visibilitychange
34754          * Fires when this region is shown or hidden 
34755          * @param {Roo.LayoutRegion} this
34756          * @param {Boolean} visibility true or false
34757          */
34758         "visibilitychange" : true,
34759         /**
34760          * @event paneladded
34761          * Fires when a panel is added. 
34762          * @param {Roo.LayoutRegion} this
34763          * @param {Roo.ContentPanel} panel The panel
34764          */
34765         "paneladded" : true,
34766         /**
34767          * @event panelremoved
34768          * Fires when a panel is removed. 
34769          * @param {Roo.LayoutRegion} this
34770          * @param {Roo.ContentPanel} panel The panel
34771          */
34772         "panelremoved" : true,
34773         /**
34774          * @event beforecollapse
34775          * Fires when this region before collapse.
34776          * @param {Roo.LayoutRegion} this
34777          */
34778         "beforecollapse" : true,
34779         /**
34780          * @event collapsed
34781          * Fires when this region is collapsed.
34782          * @param {Roo.LayoutRegion} this
34783          */
34784         "collapsed" : true,
34785         /**
34786          * @event expanded
34787          * Fires when this region is expanded.
34788          * @param {Roo.LayoutRegion} this
34789          */
34790         "expanded" : true,
34791         /**
34792          * @event slideshow
34793          * Fires when this region is slid into view.
34794          * @param {Roo.LayoutRegion} this
34795          */
34796         "slideshow" : true,
34797         /**
34798          * @event slidehide
34799          * Fires when this region slides out of view. 
34800          * @param {Roo.LayoutRegion} this
34801          */
34802         "slidehide" : true,
34803         /**
34804          * @event panelactivated
34805          * Fires when a panel is activated. 
34806          * @param {Roo.LayoutRegion} this
34807          * @param {Roo.ContentPanel} panel The activated panel
34808          */
34809         "panelactivated" : true,
34810         /**
34811          * @event resized
34812          * Fires when the user resizes this region. 
34813          * @param {Roo.LayoutRegion} this
34814          * @param {Number} newSize The new size (width for east/west, height for north/south)
34815          */
34816         "resized" : true
34817     };
34818     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34819     this.panels = new Roo.util.MixedCollection();
34820     this.panels.getKey = this.getPanelId.createDelegate(this);
34821     this.box = null;
34822     this.activePanel = null;
34823     // ensure listeners are added...
34824     
34825     if (config.listeners || config.events) {
34826         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34827             listeners : config.listeners || {},
34828             events : config.events || {}
34829         });
34830     }
34831     
34832     if(skipConfig !== true){
34833         this.applyConfig(config);
34834     }
34835 };
34836
34837 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34838 {
34839     getPanelId : function(p){
34840         return p.getId();
34841     },
34842     
34843     applyConfig : function(config){
34844         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34845         this.config = config;
34846         
34847     },
34848     
34849     /**
34850      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34851      * the width, for horizontal (north, south) the height.
34852      * @param {Number} newSize The new width or height
34853      */
34854     resizeTo : function(newSize){
34855         var el = this.el ? this.el :
34856                  (this.activePanel ? this.activePanel.getEl() : null);
34857         if(el){
34858             switch(this.position){
34859                 case "east":
34860                 case "west":
34861                     el.setWidth(newSize);
34862                     this.fireEvent("resized", this, newSize);
34863                 break;
34864                 case "north":
34865                 case "south":
34866                     el.setHeight(newSize);
34867                     this.fireEvent("resized", this, newSize);
34868                 break;                
34869             }
34870         }
34871     },
34872     
34873     getBox : function(){
34874         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34875     },
34876     
34877     getMargins : function(){
34878         return this.margins;
34879     },
34880     
34881     updateBox : function(box){
34882         this.box = box;
34883         var el = this.activePanel.getEl();
34884         el.dom.style.left = box.x + "px";
34885         el.dom.style.top = box.y + "px";
34886         this.activePanel.setSize(box.width, box.height);
34887     },
34888     
34889     /**
34890      * Returns the container element for this region.
34891      * @return {Roo.Element}
34892      */
34893     getEl : function(){
34894         return this.activePanel;
34895     },
34896     
34897     /**
34898      * Returns true if this region is currently visible.
34899      * @return {Boolean}
34900      */
34901     isVisible : function(){
34902         return this.activePanel ? true : false;
34903     },
34904     
34905     setActivePanel : function(panel){
34906         panel = this.getPanel(panel);
34907         if(this.activePanel && this.activePanel != panel){
34908             this.activePanel.setActiveState(false);
34909             this.activePanel.getEl().setLeftTop(-10000,-10000);
34910         }
34911         this.activePanel = panel;
34912         panel.setActiveState(true);
34913         if(this.box){
34914             panel.setSize(this.box.width, this.box.height);
34915         }
34916         this.fireEvent("panelactivated", this, panel);
34917         this.fireEvent("invalidated");
34918     },
34919     
34920     /**
34921      * Show the specified panel.
34922      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34923      * @return {Roo.ContentPanel} The shown panel or null
34924      */
34925     showPanel : function(panel){
34926         panel = this.getPanel(panel);
34927         if(panel){
34928             this.setActivePanel(panel);
34929         }
34930         return panel;
34931     },
34932     
34933     /**
34934      * Get the active panel for this region.
34935      * @return {Roo.ContentPanel} The active panel or null
34936      */
34937     getActivePanel : function(){
34938         return this.activePanel;
34939     },
34940     
34941     /**
34942      * Add the passed ContentPanel(s)
34943      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34944      * @return {Roo.ContentPanel} The panel added (if only one was added)
34945      */
34946     add : function(panel){
34947         if(arguments.length > 1){
34948             for(var i = 0, len = arguments.length; i < len; i++) {
34949                 this.add(arguments[i]);
34950             }
34951             return null;
34952         }
34953         if(this.hasPanel(panel)){
34954             this.showPanel(panel);
34955             return panel;
34956         }
34957         var el = panel.getEl();
34958         if(el.dom.parentNode != this.mgr.el.dom){
34959             this.mgr.el.dom.appendChild(el.dom);
34960         }
34961         if(panel.setRegion){
34962             panel.setRegion(this);
34963         }
34964         this.panels.add(panel);
34965         el.setStyle("position", "absolute");
34966         if(!panel.background){
34967             this.setActivePanel(panel);
34968             if(this.config.initialSize && this.panels.getCount()==1){
34969                 this.resizeTo(this.config.initialSize);
34970             }
34971         }
34972         this.fireEvent("paneladded", this, panel);
34973         return panel;
34974     },
34975     
34976     /**
34977      * Returns true if the panel is in this region.
34978      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34979      * @return {Boolean}
34980      */
34981     hasPanel : function(panel){
34982         if(typeof panel == "object"){ // must be panel obj
34983             panel = panel.getId();
34984         }
34985         return this.getPanel(panel) ? true : false;
34986     },
34987     
34988     /**
34989      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34990      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34991      * @param {Boolean} preservePanel Overrides the config preservePanel option
34992      * @return {Roo.ContentPanel} The panel that was removed
34993      */
34994     remove : function(panel, preservePanel){
34995         panel = this.getPanel(panel);
34996         if(!panel){
34997             return null;
34998         }
34999         var e = {};
35000         this.fireEvent("beforeremove", this, panel, e);
35001         if(e.cancel === true){
35002             return null;
35003         }
35004         var panelId = panel.getId();
35005         this.panels.removeKey(panelId);
35006         return panel;
35007     },
35008     
35009     /**
35010      * Returns the panel specified or null if it's not in this region.
35011      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35012      * @return {Roo.ContentPanel}
35013      */
35014     getPanel : function(id){
35015         if(typeof id == "object"){ // must be panel obj
35016             return id;
35017         }
35018         return this.panels.get(id);
35019     },
35020     
35021     /**
35022      * Returns this regions position (north/south/east/west/center).
35023      * @return {String} 
35024      */
35025     getPosition: function(){
35026         return this.position;    
35027     }
35028 });/*
35029  * Based on:
35030  * Ext JS Library 1.1.1
35031  * Copyright(c) 2006-2007, Ext JS, LLC.
35032  *
35033  * Originally Released Under LGPL - original licence link has changed is not relivant.
35034  *
35035  * Fork - LGPL
35036  * <script type="text/javascript">
35037  */
35038  
35039 /**
35040  * @class Roo.bootstrap.layout.Region
35041  * @extends Roo.bootstrap.layout.Basic
35042  * This class represents a region in a layout manager.
35043  
35044  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35045  * @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})
35046  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35047  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35048  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35049  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35050  * @cfg {String}    title           The title for the region (overrides panel titles)
35051  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35052  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35053  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35054  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35055  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35056  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35057  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35058  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35059  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35060  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35061
35062  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35063  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35064  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35065  * @cfg {Number}    width           For East/West panels
35066  * @cfg {Number}    height          For North/South panels
35067  * @cfg {Boolean}   split           To show the splitter
35068  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35069  * 
35070  * @cfg {string}   cls             Extra CSS classes to add to region
35071  * 
35072  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35073  * @cfg {string}   region  the region that it inhabits..
35074  *
35075
35076  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35077  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35078
35079  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35080  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35081  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35082  */
35083 Roo.bootstrap.layout.Region = function(config)
35084 {
35085     this.applyConfig(config);
35086
35087     var mgr = config.mgr;
35088     var pos = config.region;
35089     config.skipConfig = true;
35090     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35091     
35092     if (mgr.el) {
35093         this.onRender(mgr.el);   
35094     }
35095      
35096     this.visible = true;
35097     this.collapsed = false;
35098     this.unrendered_panels = [];
35099 };
35100
35101 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35102
35103     position: '', // set by wrapper (eg. north/south etc..)
35104     unrendered_panels : null,  // unrendered panels.
35105     createBody : function(){
35106         /** This region's body element 
35107         * @type Roo.Element */
35108         this.bodyEl = this.el.createChild({
35109                 tag: "div",
35110                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35111         });
35112     },
35113
35114     onRender: function(ctr, pos)
35115     {
35116         var dh = Roo.DomHelper;
35117         /** This region's container element 
35118         * @type Roo.Element */
35119         this.el = dh.append(ctr.dom, {
35120                 tag: "div",
35121                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35122             }, true);
35123         /** This region's title element 
35124         * @type Roo.Element */
35125     
35126         this.titleEl = dh.append(this.el.dom,
35127             {
35128                     tag: "div",
35129                     unselectable: "on",
35130                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35131                     children:[
35132                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35133                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35134                     ]}, true);
35135         
35136         this.titleEl.enableDisplayMode();
35137         /** This region's title text element 
35138         * @type HTMLElement */
35139         this.titleTextEl = this.titleEl.dom.firstChild;
35140         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35141         /*
35142         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35143         this.closeBtn.enableDisplayMode();
35144         this.closeBtn.on("click", this.closeClicked, this);
35145         this.closeBtn.hide();
35146     */
35147         this.createBody(this.config);
35148         if(this.config.hideWhenEmpty){
35149             this.hide();
35150             this.on("paneladded", this.validateVisibility, this);
35151             this.on("panelremoved", this.validateVisibility, this);
35152         }
35153         if(this.autoScroll){
35154             this.bodyEl.setStyle("overflow", "auto");
35155         }else{
35156             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35157         }
35158         //if(c.titlebar !== false){
35159             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35160                 this.titleEl.hide();
35161             }else{
35162                 this.titleEl.show();
35163                 if(this.config.title){
35164                     this.titleTextEl.innerHTML = this.config.title;
35165                 }
35166             }
35167         //}
35168         if(this.config.collapsed){
35169             this.collapse(true);
35170         }
35171         if(this.config.hidden){
35172             this.hide();
35173         }
35174         
35175         if (this.unrendered_panels && this.unrendered_panels.length) {
35176             for (var i =0;i< this.unrendered_panels.length; i++) {
35177                 this.add(this.unrendered_panels[i]);
35178             }
35179             this.unrendered_panels = null;
35180             
35181         }
35182         
35183     },
35184     
35185     applyConfig : function(c)
35186     {
35187         /*
35188          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35189             var dh = Roo.DomHelper;
35190             if(c.titlebar !== false){
35191                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35192                 this.collapseBtn.on("click", this.collapse, this);
35193                 this.collapseBtn.enableDisplayMode();
35194                 /*
35195                 if(c.showPin === true || this.showPin){
35196                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35197                     this.stickBtn.enableDisplayMode();
35198                     this.stickBtn.on("click", this.expand, this);
35199                     this.stickBtn.hide();
35200                 }
35201                 
35202             }
35203             */
35204             /** This region's collapsed element
35205             * @type Roo.Element */
35206             /*
35207              *
35208             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35209                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35210             ]}, true);
35211             
35212             if(c.floatable !== false){
35213                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35214                this.collapsedEl.on("click", this.collapseClick, this);
35215             }
35216
35217             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35218                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35219                    id: "message", unselectable: "on", style:{"float":"left"}});
35220                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35221              }
35222             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35223             this.expandBtn.on("click", this.expand, this);
35224             
35225         }
35226         
35227         if(this.collapseBtn){
35228             this.collapseBtn.setVisible(c.collapsible == true);
35229         }
35230         
35231         this.cmargins = c.cmargins || this.cmargins ||
35232                          (this.position == "west" || this.position == "east" ?
35233                              {top: 0, left: 2, right:2, bottom: 0} :
35234                              {top: 2, left: 0, right:0, bottom: 2});
35235         */
35236         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35237         
35238         
35239         this.bottomTabs = c.tabPosition != "top";
35240         
35241         this.autoScroll = c.autoScroll || false;
35242         
35243         
35244        
35245         
35246         this.duration = c.duration || .30;
35247         this.slideDuration = c.slideDuration || .45;
35248         this.config = c;
35249        
35250     },
35251     /**
35252      * Returns true if this region is currently visible.
35253      * @return {Boolean}
35254      */
35255     isVisible : function(){
35256         return this.visible;
35257     },
35258
35259     /**
35260      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35261      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35262      */
35263     //setCollapsedTitle : function(title){
35264     //    title = title || "&#160;";
35265      //   if(this.collapsedTitleTextEl){
35266       //      this.collapsedTitleTextEl.innerHTML = title;
35267        // }
35268     //},
35269
35270     getBox : function(){
35271         var b;
35272       //  if(!this.collapsed){
35273             b = this.el.getBox(false, true);
35274        // }else{
35275           //  b = this.collapsedEl.getBox(false, true);
35276         //}
35277         return b;
35278     },
35279
35280     getMargins : function(){
35281         return this.margins;
35282         //return this.collapsed ? this.cmargins : this.margins;
35283     },
35284 /*
35285     highlight : function(){
35286         this.el.addClass("x-layout-panel-dragover");
35287     },
35288
35289     unhighlight : function(){
35290         this.el.removeClass("x-layout-panel-dragover");
35291     },
35292 */
35293     updateBox : function(box)
35294     {
35295         if (!this.bodyEl) {
35296             return; // not rendered yet..
35297         }
35298         
35299         this.box = box;
35300         if(!this.collapsed){
35301             this.el.dom.style.left = box.x + "px";
35302             this.el.dom.style.top = box.y + "px";
35303             this.updateBody(box.width, box.height);
35304         }else{
35305             this.collapsedEl.dom.style.left = box.x + "px";
35306             this.collapsedEl.dom.style.top = box.y + "px";
35307             this.collapsedEl.setSize(box.width, box.height);
35308         }
35309         if(this.tabs){
35310             this.tabs.autoSizeTabs();
35311         }
35312     },
35313
35314     updateBody : function(w, h)
35315     {
35316         if(w !== null){
35317             this.el.setWidth(w);
35318             w -= this.el.getBorderWidth("rl");
35319             if(this.config.adjustments){
35320                 w += this.config.adjustments[0];
35321             }
35322         }
35323         if(h !== null && h > 0){
35324             this.el.setHeight(h);
35325             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35326             h -= this.el.getBorderWidth("tb");
35327             if(this.config.adjustments){
35328                 h += this.config.adjustments[1];
35329             }
35330             this.bodyEl.setHeight(h);
35331             if(this.tabs){
35332                 h = this.tabs.syncHeight(h);
35333             }
35334         }
35335         if(this.panelSize){
35336             w = w !== null ? w : this.panelSize.width;
35337             h = h !== null ? h : this.panelSize.height;
35338         }
35339         if(this.activePanel){
35340             var el = this.activePanel.getEl();
35341             w = w !== null ? w : el.getWidth();
35342             h = h !== null ? h : el.getHeight();
35343             this.panelSize = {width: w, height: h};
35344             this.activePanel.setSize(w, h);
35345         }
35346         if(Roo.isIE && this.tabs){
35347             this.tabs.el.repaint();
35348         }
35349     },
35350
35351     /**
35352      * Returns the container element for this region.
35353      * @return {Roo.Element}
35354      */
35355     getEl : function(){
35356         return this.el;
35357     },
35358
35359     /**
35360      * Hides this region.
35361      */
35362     hide : function(){
35363         //if(!this.collapsed){
35364             this.el.dom.style.left = "-2000px";
35365             this.el.hide();
35366         //}else{
35367          //   this.collapsedEl.dom.style.left = "-2000px";
35368          //   this.collapsedEl.hide();
35369        // }
35370         this.visible = false;
35371         this.fireEvent("visibilitychange", this, false);
35372     },
35373
35374     /**
35375      * Shows this region if it was previously hidden.
35376      */
35377     show : function(){
35378         //if(!this.collapsed){
35379             this.el.show();
35380         //}else{
35381         //    this.collapsedEl.show();
35382        // }
35383         this.visible = true;
35384         this.fireEvent("visibilitychange", this, true);
35385     },
35386 /*
35387     closeClicked : function(){
35388         if(this.activePanel){
35389             this.remove(this.activePanel);
35390         }
35391     },
35392
35393     collapseClick : function(e){
35394         if(this.isSlid){
35395            e.stopPropagation();
35396            this.slideIn();
35397         }else{
35398            e.stopPropagation();
35399            this.slideOut();
35400         }
35401     },
35402 */
35403     /**
35404      * Collapses this region.
35405      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35406      */
35407     /*
35408     collapse : function(skipAnim, skipCheck = false){
35409         if(this.collapsed) {
35410             return;
35411         }
35412         
35413         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35414             
35415             this.collapsed = true;
35416             if(this.split){
35417                 this.split.el.hide();
35418             }
35419             if(this.config.animate && skipAnim !== true){
35420                 this.fireEvent("invalidated", this);
35421                 this.animateCollapse();
35422             }else{
35423                 this.el.setLocation(-20000,-20000);
35424                 this.el.hide();
35425                 this.collapsedEl.show();
35426                 this.fireEvent("collapsed", this);
35427                 this.fireEvent("invalidated", this);
35428             }
35429         }
35430         
35431     },
35432 */
35433     animateCollapse : function(){
35434         // overridden
35435     },
35436
35437     /**
35438      * Expands this region if it was previously collapsed.
35439      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35440      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35441      */
35442     /*
35443     expand : function(e, skipAnim){
35444         if(e) {
35445             e.stopPropagation();
35446         }
35447         if(!this.collapsed || this.el.hasActiveFx()) {
35448             return;
35449         }
35450         if(this.isSlid){
35451             this.afterSlideIn();
35452             skipAnim = true;
35453         }
35454         this.collapsed = false;
35455         if(this.config.animate && skipAnim !== true){
35456             this.animateExpand();
35457         }else{
35458             this.el.show();
35459             if(this.split){
35460                 this.split.el.show();
35461             }
35462             this.collapsedEl.setLocation(-2000,-2000);
35463             this.collapsedEl.hide();
35464             this.fireEvent("invalidated", this);
35465             this.fireEvent("expanded", this);
35466         }
35467     },
35468 */
35469     animateExpand : function(){
35470         // overridden
35471     },
35472
35473     initTabs : function()
35474     {
35475         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35476         
35477         var ts = new Roo.bootstrap.panel.Tabs({
35478                 el: this.bodyEl.dom,
35479                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35480                 disableTooltips: this.config.disableTabTips,
35481                 toolbar : this.config.toolbar
35482             });
35483         
35484         if(this.config.hideTabs){
35485             ts.stripWrap.setDisplayed(false);
35486         }
35487         this.tabs = ts;
35488         ts.resizeTabs = this.config.resizeTabs === true;
35489         ts.minTabWidth = this.config.minTabWidth || 40;
35490         ts.maxTabWidth = this.config.maxTabWidth || 250;
35491         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35492         ts.monitorResize = false;
35493         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35494         ts.bodyEl.addClass('roo-layout-tabs-body');
35495         this.panels.each(this.initPanelAsTab, this);
35496     },
35497
35498     initPanelAsTab : function(panel){
35499         var ti = this.tabs.addTab(
35500             panel.getEl().id,
35501             panel.getTitle(),
35502             null,
35503             this.config.closeOnTab && panel.isClosable(),
35504             panel.tpl
35505         );
35506         if(panel.tabTip !== undefined){
35507             ti.setTooltip(panel.tabTip);
35508         }
35509         ti.on("activate", function(){
35510               this.setActivePanel(panel);
35511         }, this);
35512         
35513         if(this.config.closeOnTab){
35514             ti.on("beforeclose", function(t, e){
35515                 e.cancel = true;
35516                 this.remove(panel);
35517             }, this);
35518         }
35519         
35520         panel.tabItem = ti;
35521         
35522         return ti;
35523     },
35524
35525     updatePanelTitle : function(panel, title)
35526     {
35527         if(this.activePanel == panel){
35528             this.updateTitle(title);
35529         }
35530         if(this.tabs){
35531             var ti = this.tabs.getTab(panel.getEl().id);
35532             ti.setText(title);
35533             if(panel.tabTip !== undefined){
35534                 ti.setTooltip(panel.tabTip);
35535             }
35536         }
35537     },
35538
35539     updateTitle : function(title){
35540         if(this.titleTextEl && !this.config.title){
35541             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35542         }
35543     },
35544
35545     setActivePanel : function(panel)
35546     {
35547         panel = this.getPanel(panel);
35548         if(this.activePanel && this.activePanel != panel){
35549             if(this.activePanel.setActiveState(false) === false){
35550                 return;
35551             }
35552         }
35553         this.activePanel = panel;
35554         panel.setActiveState(true);
35555         if(this.panelSize){
35556             panel.setSize(this.panelSize.width, this.panelSize.height);
35557         }
35558         if(this.closeBtn){
35559             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35560         }
35561         this.updateTitle(panel.getTitle());
35562         if(this.tabs){
35563             this.fireEvent("invalidated", this);
35564         }
35565         this.fireEvent("panelactivated", this, panel);
35566     },
35567
35568     /**
35569      * Shows the specified panel.
35570      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35571      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35572      */
35573     showPanel : function(panel)
35574     {
35575         panel = this.getPanel(panel);
35576         if(panel){
35577             if(this.tabs){
35578                 var tab = this.tabs.getTab(panel.getEl().id);
35579                 if(tab.isHidden()){
35580                     this.tabs.unhideTab(tab.id);
35581                 }
35582                 tab.activate();
35583             }else{
35584                 this.setActivePanel(panel);
35585             }
35586         }
35587         return panel;
35588     },
35589
35590     /**
35591      * Get the active panel for this region.
35592      * @return {Roo.ContentPanel} The active panel or null
35593      */
35594     getActivePanel : function(){
35595         return this.activePanel;
35596     },
35597
35598     validateVisibility : function(){
35599         if(this.panels.getCount() < 1){
35600             this.updateTitle("&#160;");
35601             this.closeBtn.hide();
35602             this.hide();
35603         }else{
35604             if(!this.isVisible()){
35605                 this.show();
35606             }
35607         }
35608     },
35609
35610     /**
35611      * Adds the passed ContentPanel(s) to this region.
35612      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35613      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35614      */
35615     add : function(panel)
35616     {
35617         if(arguments.length > 1){
35618             for(var i = 0, len = arguments.length; i < len; i++) {
35619                 this.add(arguments[i]);
35620             }
35621             return null;
35622         }
35623         
35624         // if we have not been rendered yet, then we can not really do much of this..
35625         if (!this.bodyEl) {
35626             this.unrendered_panels.push(panel);
35627             return panel;
35628         }
35629         
35630         
35631         
35632         
35633         if(this.hasPanel(panel)){
35634             this.showPanel(panel);
35635             return panel;
35636         }
35637         panel.setRegion(this);
35638         this.panels.add(panel);
35639        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35640             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35641             // and hide them... ???
35642             this.bodyEl.dom.appendChild(panel.getEl().dom);
35643             if(panel.background !== true){
35644                 this.setActivePanel(panel);
35645             }
35646             this.fireEvent("paneladded", this, panel);
35647             return panel;
35648         }
35649         */
35650         if(!this.tabs){
35651             this.initTabs();
35652         }else{
35653             this.initPanelAsTab(panel);
35654         }
35655         
35656         
35657         if(panel.background !== true){
35658             this.tabs.activate(panel.getEl().id);
35659         }
35660         this.fireEvent("paneladded", this, panel);
35661         return panel;
35662     },
35663
35664     /**
35665      * Hides the tab for the specified panel.
35666      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35667      */
35668     hidePanel : function(panel){
35669         if(this.tabs && (panel = this.getPanel(panel))){
35670             this.tabs.hideTab(panel.getEl().id);
35671         }
35672     },
35673
35674     /**
35675      * Unhides the tab for a previously hidden panel.
35676      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35677      */
35678     unhidePanel : function(panel){
35679         if(this.tabs && (panel = this.getPanel(panel))){
35680             this.tabs.unhideTab(panel.getEl().id);
35681         }
35682     },
35683
35684     clearPanels : function(){
35685         while(this.panels.getCount() > 0){
35686              this.remove(this.panels.first());
35687         }
35688     },
35689
35690     /**
35691      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35692      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35693      * @param {Boolean} preservePanel Overrides the config preservePanel option
35694      * @return {Roo.ContentPanel} The panel that was removed
35695      */
35696     remove : function(panel, preservePanel)
35697     {
35698         panel = this.getPanel(panel);
35699         if(!panel){
35700             return null;
35701         }
35702         var e = {};
35703         this.fireEvent("beforeremove", this, panel, e);
35704         if(e.cancel === true){
35705             return null;
35706         }
35707         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35708         var panelId = panel.getId();
35709         this.panels.removeKey(panelId);
35710         if(preservePanel){
35711             document.body.appendChild(panel.getEl().dom);
35712         }
35713         if(this.tabs){
35714             this.tabs.removeTab(panel.getEl().id);
35715         }else if (!preservePanel){
35716             this.bodyEl.dom.removeChild(panel.getEl().dom);
35717         }
35718         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35719             var p = this.panels.first();
35720             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35721             tempEl.appendChild(p.getEl().dom);
35722             this.bodyEl.update("");
35723             this.bodyEl.dom.appendChild(p.getEl().dom);
35724             tempEl = null;
35725             this.updateTitle(p.getTitle());
35726             this.tabs = null;
35727             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35728             this.setActivePanel(p);
35729         }
35730         panel.setRegion(null);
35731         if(this.activePanel == panel){
35732             this.activePanel = null;
35733         }
35734         if(this.config.autoDestroy !== false && preservePanel !== true){
35735             try{panel.destroy();}catch(e){}
35736         }
35737         this.fireEvent("panelremoved", this, panel);
35738         return panel;
35739     },
35740
35741     /**
35742      * Returns the TabPanel component used by this region
35743      * @return {Roo.TabPanel}
35744      */
35745     getTabs : function(){
35746         return this.tabs;
35747     },
35748
35749     createTool : function(parentEl, className){
35750         var btn = Roo.DomHelper.append(parentEl, {
35751             tag: "div",
35752             cls: "x-layout-tools-button",
35753             children: [ {
35754                 tag: "div",
35755                 cls: "roo-layout-tools-button-inner " + className,
35756                 html: "&#160;"
35757             }]
35758         }, true);
35759         btn.addClassOnOver("roo-layout-tools-button-over");
35760         return btn;
35761     }
35762 });/*
35763  * Based on:
35764  * Ext JS Library 1.1.1
35765  * Copyright(c) 2006-2007, Ext JS, LLC.
35766  *
35767  * Originally Released Under LGPL - original licence link has changed is not relivant.
35768  *
35769  * Fork - LGPL
35770  * <script type="text/javascript">
35771  */
35772  
35773
35774
35775 /**
35776  * @class Roo.SplitLayoutRegion
35777  * @extends Roo.LayoutRegion
35778  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35779  */
35780 Roo.bootstrap.layout.Split = function(config){
35781     this.cursor = config.cursor;
35782     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35783 };
35784
35785 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35786 {
35787     splitTip : "Drag to resize.",
35788     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35789     useSplitTips : false,
35790
35791     applyConfig : function(config){
35792         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35793     },
35794     
35795     onRender : function(ctr,pos) {
35796         
35797         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35798         if(!this.config.split){
35799             return;
35800         }
35801         if(!this.split){
35802             
35803             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35804                             tag: "div",
35805                             id: this.el.id + "-split",
35806                             cls: "roo-layout-split roo-layout-split-"+this.position,
35807                             html: "&#160;"
35808             });
35809             /** The SplitBar for this region 
35810             * @type Roo.SplitBar */
35811             // does not exist yet...
35812             Roo.log([this.position, this.orientation]);
35813             
35814             this.split = new Roo.bootstrap.SplitBar({
35815                 dragElement : splitEl,
35816                 resizingElement: this.el,
35817                 orientation : this.orientation
35818             });
35819             
35820             this.split.on("moved", this.onSplitMove, this);
35821             this.split.useShim = this.config.useShim === true;
35822             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35823             if(this.useSplitTips){
35824                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35825             }
35826             //if(config.collapsible){
35827             //    this.split.el.on("dblclick", this.collapse,  this);
35828             //}
35829         }
35830         if(typeof this.config.minSize != "undefined"){
35831             this.split.minSize = this.config.minSize;
35832         }
35833         if(typeof this.config.maxSize != "undefined"){
35834             this.split.maxSize = this.config.maxSize;
35835         }
35836         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35837             this.hideSplitter();
35838         }
35839         
35840     },
35841
35842     getHMaxSize : function(){
35843          var cmax = this.config.maxSize || 10000;
35844          var center = this.mgr.getRegion("center");
35845          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35846     },
35847
35848     getVMaxSize : function(){
35849          var cmax = this.config.maxSize || 10000;
35850          var center = this.mgr.getRegion("center");
35851          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35852     },
35853
35854     onSplitMove : function(split, newSize){
35855         this.fireEvent("resized", this, newSize);
35856     },
35857     
35858     /** 
35859      * Returns the {@link Roo.SplitBar} for this region.
35860      * @return {Roo.SplitBar}
35861      */
35862     getSplitBar : function(){
35863         return this.split;
35864     },
35865     
35866     hide : function(){
35867         this.hideSplitter();
35868         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35869     },
35870
35871     hideSplitter : function(){
35872         if(this.split){
35873             this.split.el.setLocation(-2000,-2000);
35874             this.split.el.hide();
35875         }
35876     },
35877
35878     show : function(){
35879         if(this.split){
35880             this.split.el.show();
35881         }
35882         Roo.bootstrap.layout.Split.superclass.show.call(this);
35883     },
35884     
35885     beforeSlide: function(){
35886         if(Roo.isGecko){// firefox overflow auto bug workaround
35887             this.bodyEl.clip();
35888             if(this.tabs) {
35889                 this.tabs.bodyEl.clip();
35890             }
35891             if(this.activePanel){
35892                 this.activePanel.getEl().clip();
35893                 
35894                 if(this.activePanel.beforeSlide){
35895                     this.activePanel.beforeSlide();
35896                 }
35897             }
35898         }
35899     },
35900     
35901     afterSlide : function(){
35902         if(Roo.isGecko){// firefox overflow auto bug workaround
35903             this.bodyEl.unclip();
35904             if(this.tabs) {
35905                 this.tabs.bodyEl.unclip();
35906             }
35907             if(this.activePanel){
35908                 this.activePanel.getEl().unclip();
35909                 if(this.activePanel.afterSlide){
35910                     this.activePanel.afterSlide();
35911                 }
35912             }
35913         }
35914     },
35915
35916     initAutoHide : function(){
35917         if(this.autoHide !== false){
35918             if(!this.autoHideHd){
35919                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35920                 this.autoHideHd = {
35921                     "mouseout": function(e){
35922                         if(!e.within(this.el, true)){
35923                             st.delay(500);
35924                         }
35925                     },
35926                     "mouseover" : function(e){
35927                         st.cancel();
35928                     },
35929                     scope : this
35930                 };
35931             }
35932             this.el.on(this.autoHideHd);
35933         }
35934     },
35935
35936     clearAutoHide : function(){
35937         if(this.autoHide !== false){
35938             this.el.un("mouseout", this.autoHideHd.mouseout);
35939             this.el.un("mouseover", this.autoHideHd.mouseover);
35940         }
35941     },
35942
35943     clearMonitor : function(){
35944         Roo.get(document).un("click", this.slideInIf, this);
35945     },
35946
35947     // these names are backwards but not changed for compat
35948     slideOut : function(){
35949         if(this.isSlid || this.el.hasActiveFx()){
35950             return;
35951         }
35952         this.isSlid = true;
35953         if(this.collapseBtn){
35954             this.collapseBtn.hide();
35955         }
35956         this.closeBtnState = this.closeBtn.getStyle('display');
35957         this.closeBtn.hide();
35958         if(this.stickBtn){
35959             this.stickBtn.show();
35960         }
35961         this.el.show();
35962         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35963         this.beforeSlide();
35964         this.el.setStyle("z-index", 10001);
35965         this.el.slideIn(this.getSlideAnchor(), {
35966             callback: function(){
35967                 this.afterSlide();
35968                 this.initAutoHide();
35969                 Roo.get(document).on("click", this.slideInIf, this);
35970                 this.fireEvent("slideshow", this);
35971             },
35972             scope: this,
35973             block: true
35974         });
35975     },
35976
35977     afterSlideIn : function(){
35978         this.clearAutoHide();
35979         this.isSlid = false;
35980         this.clearMonitor();
35981         this.el.setStyle("z-index", "");
35982         if(this.collapseBtn){
35983             this.collapseBtn.show();
35984         }
35985         this.closeBtn.setStyle('display', this.closeBtnState);
35986         if(this.stickBtn){
35987             this.stickBtn.hide();
35988         }
35989         this.fireEvent("slidehide", this);
35990     },
35991
35992     slideIn : function(cb){
35993         if(!this.isSlid || this.el.hasActiveFx()){
35994             Roo.callback(cb);
35995             return;
35996         }
35997         this.isSlid = false;
35998         this.beforeSlide();
35999         this.el.slideOut(this.getSlideAnchor(), {
36000             callback: function(){
36001                 this.el.setLeftTop(-10000, -10000);
36002                 this.afterSlide();
36003                 this.afterSlideIn();
36004                 Roo.callback(cb);
36005             },
36006             scope: this,
36007             block: true
36008         });
36009     },
36010     
36011     slideInIf : function(e){
36012         if(!e.within(this.el)){
36013             this.slideIn();
36014         }
36015     },
36016
36017     animateCollapse : function(){
36018         this.beforeSlide();
36019         this.el.setStyle("z-index", 20000);
36020         var anchor = this.getSlideAnchor();
36021         this.el.slideOut(anchor, {
36022             callback : function(){
36023                 this.el.setStyle("z-index", "");
36024                 this.collapsedEl.slideIn(anchor, {duration:.3});
36025                 this.afterSlide();
36026                 this.el.setLocation(-10000,-10000);
36027                 this.el.hide();
36028                 this.fireEvent("collapsed", this);
36029             },
36030             scope: this,
36031             block: true
36032         });
36033     },
36034
36035     animateExpand : function(){
36036         this.beforeSlide();
36037         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36038         this.el.setStyle("z-index", 20000);
36039         this.collapsedEl.hide({
36040             duration:.1
36041         });
36042         this.el.slideIn(this.getSlideAnchor(), {
36043             callback : function(){
36044                 this.el.setStyle("z-index", "");
36045                 this.afterSlide();
36046                 if(this.split){
36047                     this.split.el.show();
36048                 }
36049                 this.fireEvent("invalidated", this);
36050                 this.fireEvent("expanded", this);
36051             },
36052             scope: this,
36053             block: true
36054         });
36055     },
36056
36057     anchors : {
36058         "west" : "left",
36059         "east" : "right",
36060         "north" : "top",
36061         "south" : "bottom"
36062     },
36063
36064     sanchors : {
36065         "west" : "l",
36066         "east" : "r",
36067         "north" : "t",
36068         "south" : "b"
36069     },
36070
36071     canchors : {
36072         "west" : "tl-tr",
36073         "east" : "tr-tl",
36074         "north" : "tl-bl",
36075         "south" : "bl-tl"
36076     },
36077
36078     getAnchor : function(){
36079         return this.anchors[this.position];
36080     },
36081
36082     getCollapseAnchor : function(){
36083         return this.canchors[this.position];
36084     },
36085
36086     getSlideAnchor : function(){
36087         return this.sanchors[this.position];
36088     },
36089
36090     getAlignAdj : function(){
36091         var cm = this.cmargins;
36092         switch(this.position){
36093             case "west":
36094                 return [0, 0];
36095             break;
36096             case "east":
36097                 return [0, 0];
36098             break;
36099             case "north":
36100                 return [0, 0];
36101             break;
36102             case "south":
36103                 return [0, 0];
36104             break;
36105         }
36106     },
36107
36108     getExpandAdj : function(){
36109         var c = this.collapsedEl, cm = this.cmargins;
36110         switch(this.position){
36111             case "west":
36112                 return [-(cm.right+c.getWidth()+cm.left), 0];
36113             break;
36114             case "east":
36115                 return [cm.right+c.getWidth()+cm.left, 0];
36116             break;
36117             case "north":
36118                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36119             break;
36120             case "south":
36121                 return [0, cm.top+cm.bottom+c.getHeight()];
36122             break;
36123         }
36124     }
36125 });/*
36126  * Based on:
36127  * Ext JS Library 1.1.1
36128  * Copyright(c) 2006-2007, Ext JS, LLC.
36129  *
36130  * Originally Released Under LGPL - original licence link has changed is not relivant.
36131  *
36132  * Fork - LGPL
36133  * <script type="text/javascript">
36134  */
36135 /*
36136  * These classes are private internal classes
36137  */
36138 Roo.bootstrap.layout.Center = function(config){
36139     config.region = "center";
36140     Roo.bootstrap.layout.Region.call(this, config);
36141     this.visible = true;
36142     this.minWidth = config.minWidth || 20;
36143     this.minHeight = config.minHeight || 20;
36144 };
36145
36146 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36147     hide : function(){
36148         // center panel can't be hidden
36149     },
36150     
36151     show : function(){
36152         // center panel can't be hidden
36153     },
36154     
36155     getMinWidth: function(){
36156         return this.minWidth;
36157     },
36158     
36159     getMinHeight: function(){
36160         return this.minHeight;
36161     }
36162 });
36163
36164
36165
36166
36167  
36168
36169
36170
36171
36172
36173 Roo.bootstrap.layout.North = function(config)
36174 {
36175     config.region = 'north';
36176     config.cursor = 'n-resize';
36177     
36178     Roo.bootstrap.layout.Split.call(this, config);
36179     
36180     
36181     if(this.split){
36182         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36183         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36184         this.split.el.addClass("roo-layout-split-v");
36185     }
36186     var size = config.initialSize || config.height;
36187     if(typeof size != "undefined"){
36188         this.el.setHeight(size);
36189     }
36190 };
36191 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36192 {
36193     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36194     
36195     
36196     
36197     getBox : function(){
36198         if(this.collapsed){
36199             return this.collapsedEl.getBox();
36200         }
36201         var box = this.el.getBox();
36202         if(this.split){
36203             box.height += this.split.el.getHeight();
36204         }
36205         return box;
36206     },
36207     
36208     updateBox : function(box){
36209         if(this.split && !this.collapsed){
36210             box.height -= this.split.el.getHeight();
36211             this.split.el.setLeft(box.x);
36212             this.split.el.setTop(box.y+box.height);
36213             this.split.el.setWidth(box.width);
36214         }
36215         if(this.collapsed){
36216             this.updateBody(box.width, null);
36217         }
36218         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36219     }
36220 });
36221
36222
36223
36224
36225
36226 Roo.bootstrap.layout.South = function(config){
36227     config.region = 'south';
36228     config.cursor = 's-resize';
36229     Roo.bootstrap.layout.Split.call(this, config);
36230     if(this.split){
36231         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36232         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36233         this.split.el.addClass("roo-layout-split-v");
36234     }
36235     var size = config.initialSize || config.height;
36236     if(typeof size != "undefined"){
36237         this.el.setHeight(size);
36238     }
36239 };
36240
36241 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36242     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36243     getBox : function(){
36244         if(this.collapsed){
36245             return this.collapsedEl.getBox();
36246         }
36247         var box = this.el.getBox();
36248         if(this.split){
36249             var sh = this.split.el.getHeight();
36250             box.height += sh;
36251             box.y -= sh;
36252         }
36253         return box;
36254     },
36255     
36256     updateBox : function(box){
36257         if(this.split && !this.collapsed){
36258             var sh = this.split.el.getHeight();
36259             box.height -= sh;
36260             box.y += sh;
36261             this.split.el.setLeft(box.x);
36262             this.split.el.setTop(box.y-sh);
36263             this.split.el.setWidth(box.width);
36264         }
36265         if(this.collapsed){
36266             this.updateBody(box.width, null);
36267         }
36268         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36269     }
36270 });
36271
36272 Roo.bootstrap.layout.East = function(config){
36273     config.region = "east";
36274     config.cursor = "e-resize";
36275     Roo.bootstrap.layout.Split.call(this, config);
36276     if(this.split){
36277         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36278         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36279         this.split.el.addClass("roo-layout-split-h");
36280     }
36281     var size = config.initialSize || config.width;
36282     if(typeof size != "undefined"){
36283         this.el.setWidth(size);
36284     }
36285 };
36286 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36287     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36288     getBox : function(){
36289         if(this.collapsed){
36290             return this.collapsedEl.getBox();
36291         }
36292         var box = this.el.getBox();
36293         if(this.split){
36294             var sw = this.split.el.getWidth();
36295             box.width += sw;
36296             box.x -= sw;
36297         }
36298         return box;
36299     },
36300
36301     updateBox : function(box){
36302         if(this.split && !this.collapsed){
36303             var sw = this.split.el.getWidth();
36304             box.width -= sw;
36305             this.split.el.setLeft(box.x);
36306             this.split.el.setTop(box.y);
36307             this.split.el.setHeight(box.height);
36308             box.x += sw;
36309         }
36310         if(this.collapsed){
36311             this.updateBody(null, box.height);
36312         }
36313         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36314     }
36315 });
36316
36317 Roo.bootstrap.layout.West = function(config){
36318     config.region = "west";
36319     config.cursor = "w-resize";
36320     
36321     Roo.bootstrap.layout.Split.call(this, config);
36322     if(this.split){
36323         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36324         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36325         this.split.el.addClass("roo-layout-split-h");
36326     }
36327     
36328 };
36329 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36330     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36331     
36332     onRender: function(ctr, pos)
36333     {
36334         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36335         var size = this.config.initialSize || this.config.width;
36336         if(typeof size != "undefined"){
36337             this.el.setWidth(size);
36338         }
36339     },
36340     
36341     getBox : function(){
36342         if(this.collapsed){
36343             return this.collapsedEl.getBox();
36344         }
36345         var box = this.el.getBox();
36346         if(this.split){
36347             box.width += this.split.el.getWidth();
36348         }
36349         return box;
36350     },
36351     
36352     updateBox : function(box){
36353         if(this.split && !this.collapsed){
36354             var sw = this.split.el.getWidth();
36355             box.width -= sw;
36356             this.split.el.setLeft(box.x+box.width);
36357             this.split.el.setTop(box.y);
36358             this.split.el.setHeight(box.height);
36359         }
36360         if(this.collapsed){
36361             this.updateBody(null, box.height);
36362         }
36363         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36364     }
36365 });
36366 Roo.namespace("Roo.bootstrap.panel");/*
36367  * Based on:
36368  * Ext JS Library 1.1.1
36369  * Copyright(c) 2006-2007, Ext JS, LLC.
36370  *
36371  * Originally Released Under LGPL - original licence link has changed is not relivant.
36372  *
36373  * Fork - LGPL
36374  * <script type="text/javascript">
36375  */
36376 /**
36377  * @class Roo.ContentPanel
36378  * @extends Roo.util.Observable
36379  * A basic ContentPanel element.
36380  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36381  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36382  * @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
36383  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36384  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36385  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36386  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36387  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36388  * @cfg {String} title          The title for this panel
36389  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36390  * @cfg {String} url            Calls {@link #setUrl} with this value
36391  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36392  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36393  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36394  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36395  * @cfg {Boolean} badges render the badges
36396
36397  * @constructor
36398  * Create a new ContentPanel.
36399  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36400  * @param {String/Object} config A string to set only the title or a config object
36401  * @param {String} content (optional) Set the HTML content for this panel
36402  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36403  */
36404 Roo.bootstrap.panel.Content = function( config){
36405     
36406     this.tpl = config.tpl || false;
36407     
36408     var el = config.el;
36409     var content = config.content;
36410
36411     if(config.autoCreate){ // xtype is available if this is called from factory
36412         el = Roo.id();
36413     }
36414     this.el = Roo.get(el);
36415     if(!this.el && config && config.autoCreate){
36416         if(typeof config.autoCreate == "object"){
36417             if(!config.autoCreate.id){
36418                 config.autoCreate.id = config.id||el;
36419             }
36420             this.el = Roo.DomHelper.append(document.body,
36421                         config.autoCreate, true);
36422         }else{
36423             var elcfg =  {   tag: "div",
36424                             cls: "roo-layout-inactive-content",
36425                             id: config.id||el
36426                             };
36427             if (config.html) {
36428                 elcfg.html = config.html;
36429                 
36430             }
36431                         
36432             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36433         }
36434     } 
36435     this.closable = false;
36436     this.loaded = false;
36437     this.active = false;
36438    
36439       
36440     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36441         
36442         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36443         
36444         this.wrapEl = this.el; //this.el.wrap();
36445         var ti = [];
36446         if (config.toolbar.items) {
36447             ti = config.toolbar.items ;
36448             delete config.toolbar.items ;
36449         }
36450         
36451         var nitems = [];
36452         this.toolbar.render(this.wrapEl, 'before');
36453         for(var i =0;i < ti.length;i++) {
36454           //  Roo.log(['add child', items[i]]);
36455             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36456         }
36457         this.toolbar.items = nitems;
36458         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36459         delete config.toolbar;
36460         
36461     }
36462     /*
36463     // xtype created footer. - not sure if will work as we normally have to render first..
36464     if (this.footer && !this.footer.el && this.footer.xtype) {
36465         if (!this.wrapEl) {
36466             this.wrapEl = this.el.wrap();
36467         }
36468     
36469         this.footer.container = this.wrapEl.createChild();
36470          
36471         this.footer = Roo.factory(this.footer, Roo);
36472         
36473     }
36474     */
36475     
36476      if(typeof config == "string"){
36477         this.title = config;
36478     }else{
36479         Roo.apply(this, config);
36480     }
36481     
36482     if(this.resizeEl){
36483         this.resizeEl = Roo.get(this.resizeEl, true);
36484     }else{
36485         this.resizeEl = this.el;
36486     }
36487     // handle view.xtype
36488     
36489  
36490     
36491     
36492     this.addEvents({
36493         /**
36494          * @event activate
36495          * Fires when this panel is activated. 
36496          * @param {Roo.ContentPanel} this
36497          */
36498         "activate" : true,
36499         /**
36500          * @event deactivate
36501          * Fires when this panel is activated. 
36502          * @param {Roo.ContentPanel} this
36503          */
36504         "deactivate" : true,
36505
36506         /**
36507          * @event resize
36508          * Fires when this panel is resized if fitToFrame is true.
36509          * @param {Roo.ContentPanel} this
36510          * @param {Number} width The width after any component adjustments
36511          * @param {Number} height The height after any component adjustments
36512          */
36513         "resize" : true,
36514         
36515          /**
36516          * @event render
36517          * Fires when this tab is created
36518          * @param {Roo.ContentPanel} this
36519          */
36520         "render" : true
36521         
36522         
36523         
36524     });
36525     
36526
36527     
36528     
36529     if(this.autoScroll){
36530         this.resizeEl.setStyle("overflow", "auto");
36531     } else {
36532         // fix randome scrolling
36533         //this.el.on('scroll', function() {
36534         //    Roo.log('fix random scolling');
36535         //    this.scrollTo('top',0); 
36536         //});
36537     }
36538     content = content || this.content;
36539     if(content){
36540         this.setContent(content);
36541     }
36542     if(config && config.url){
36543         this.setUrl(this.url, this.params, this.loadOnce);
36544     }
36545     
36546     
36547     
36548     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36549     
36550     if (this.view && typeof(this.view.xtype) != 'undefined') {
36551         this.view.el = this.el.appendChild(document.createElement("div"));
36552         this.view = Roo.factory(this.view); 
36553         this.view.render  &&  this.view.render(false, '');  
36554     }
36555     
36556     
36557     this.fireEvent('render', this);
36558 };
36559
36560 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36561     
36562     tabTip : '',
36563     
36564     setRegion : function(region){
36565         this.region = region;
36566         this.setActiveClass(region && !this.background);
36567     },
36568     
36569     
36570     setActiveClass: function(state)
36571     {
36572         if(state){
36573            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36574            this.el.setStyle('position','relative');
36575         }else{
36576            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36577            this.el.setStyle('position', 'absolute');
36578         } 
36579     },
36580     
36581     /**
36582      * Returns the toolbar for this Panel if one was configured. 
36583      * @return {Roo.Toolbar} 
36584      */
36585     getToolbar : function(){
36586         return this.toolbar;
36587     },
36588     
36589     setActiveState : function(active)
36590     {
36591         this.active = active;
36592         this.setActiveClass(active);
36593         if(!active){
36594             if(this.fireEvent("deactivate", this) === false){
36595                 return false;
36596             }
36597             return true;
36598         }
36599         this.fireEvent("activate", this);
36600         return true;
36601     },
36602     /**
36603      * Updates this panel's element
36604      * @param {String} content The new content
36605      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36606     */
36607     setContent : function(content, loadScripts){
36608         this.el.update(content, loadScripts);
36609     },
36610
36611     ignoreResize : function(w, h){
36612         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36613             return true;
36614         }else{
36615             this.lastSize = {width: w, height: h};
36616             return false;
36617         }
36618     },
36619     /**
36620      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36621      * @return {Roo.UpdateManager} The UpdateManager
36622      */
36623     getUpdateManager : function(){
36624         return this.el.getUpdateManager();
36625     },
36626      /**
36627      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36628      * @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:
36629 <pre><code>
36630 panel.load({
36631     url: "your-url.php",
36632     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36633     callback: yourFunction,
36634     scope: yourObject, //(optional scope)
36635     discardUrl: false,
36636     nocache: false,
36637     text: "Loading...",
36638     timeout: 30,
36639     scripts: false
36640 });
36641 </code></pre>
36642      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36643      * 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.
36644      * @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}
36645      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36646      * @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.
36647      * @return {Roo.ContentPanel} this
36648      */
36649     load : function(){
36650         var um = this.el.getUpdateManager();
36651         um.update.apply(um, arguments);
36652         return this;
36653     },
36654
36655
36656     /**
36657      * 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.
36658      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36659      * @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)
36660      * @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)
36661      * @return {Roo.UpdateManager} The UpdateManager
36662      */
36663     setUrl : function(url, params, loadOnce){
36664         if(this.refreshDelegate){
36665             this.removeListener("activate", this.refreshDelegate);
36666         }
36667         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36668         this.on("activate", this.refreshDelegate);
36669         return this.el.getUpdateManager();
36670     },
36671     
36672     _handleRefresh : function(url, params, loadOnce){
36673         if(!loadOnce || !this.loaded){
36674             var updater = this.el.getUpdateManager();
36675             updater.update(url, params, this._setLoaded.createDelegate(this));
36676         }
36677     },
36678     
36679     _setLoaded : function(){
36680         this.loaded = true;
36681     }, 
36682     
36683     /**
36684      * Returns this panel's id
36685      * @return {String} 
36686      */
36687     getId : function(){
36688         return this.el.id;
36689     },
36690     
36691     /** 
36692      * Returns this panel's element - used by regiosn to add.
36693      * @return {Roo.Element} 
36694      */
36695     getEl : function(){
36696         return this.wrapEl || this.el;
36697     },
36698     
36699    
36700     
36701     adjustForComponents : function(width, height)
36702     {
36703         //Roo.log('adjustForComponents ');
36704         if(this.resizeEl != this.el){
36705             width -= this.el.getFrameWidth('lr');
36706             height -= this.el.getFrameWidth('tb');
36707         }
36708         if(this.toolbar){
36709             var te = this.toolbar.getEl();
36710             te.setWidth(width);
36711             height -= te.getHeight();
36712         }
36713         if(this.footer){
36714             var te = this.footer.getEl();
36715             te.setWidth(width);
36716             height -= te.getHeight();
36717         }
36718         
36719         
36720         if(this.adjustments){
36721             width += this.adjustments[0];
36722             height += this.adjustments[1];
36723         }
36724         return {"width": width, "height": height};
36725     },
36726     
36727     setSize : function(width, height){
36728         if(this.fitToFrame && !this.ignoreResize(width, height)){
36729             if(this.fitContainer && this.resizeEl != this.el){
36730                 this.el.setSize(width, height);
36731             }
36732             var size = this.adjustForComponents(width, height);
36733             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36734             this.fireEvent('resize', this, size.width, size.height);
36735         }
36736     },
36737     
36738     /**
36739      * Returns this panel's title
36740      * @return {String} 
36741      */
36742     getTitle : function(){
36743         
36744         if (typeof(this.title) != 'object') {
36745             return this.title;
36746         }
36747         
36748         var t = '';
36749         for (var k in this.title) {
36750             if (!this.title.hasOwnProperty(k)) {
36751                 continue;
36752             }
36753             
36754             if (k.indexOf('-') >= 0) {
36755                 var s = k.split('-');
36756                 for (var i = 0; i<s.length; i++) {
36757                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36758                 }
36759             } else {
36760                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36761             }
36762         }
36763         return t;
36764     },
36765     
36766     /**
36767      * Set this panel's title
36768      * @param {String} title
36769      */
36770     setTitle : function(title){
36771         this.title = title;
36772         if(this.region){
36773             this.region.updatePanelTitle(this, title);
36774         }
36775     },
36776     
36777     /**
36778      * Returns true is this panel was configured to be closable
36779      * @return {Boolean} 
36780      */
36781     isClosable : function(){
36782         return this.closable;
36783     },
36784     
36785     beforeSlide : function(){
36786         this.el.clip();
36787         this.resizeEl.clip();
36788     },
36789     
36790     afterSlide : function(){
36791         this.el.unclip();
36792         this.resizeEl.unclip();
36793     },
36794     
36795     /**
36796      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36797      *   Will fail silently if the {@link #setUrl} method has not been called.
36798      *   This does not activate the panel, just updates its content.
36799      */
36800     refresh : function(){
36801         if(this.refreshDelegate){
36802            this.loaded = false;
36803            this.refreshDelegate();
36804         }
36805     },
36806     
36807     /**
36808      * Destroys this panel
36809      */
36810     destroy : function(){
36811         this.el.removeAllListeners();
36812         var tempEl = document.createElement("span");
36813         tempEl.appendChild(this.el.dom);
36814         tempEl.innerHTML = "";
36815         this.el.remove();
36816         this.el = null;
36817     },
36818     
36819     /**
36820      * form - if the content panel contains a form - this is a reference to it.
36821      * @type {Roo.form.Form}
36822      */
36823     form : false,
36824     /**
36825      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36826      *    This contains a reference to it.
36827      * @type {Roo.View}
36828      */
36829     view : false,
36830     
36831       /**
36832      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36833      * <pre><code>
36834
36835 layout.addxtype({
36836        xtype : 'Form',
36837        items: [ .... ]
36838    }
36839 );
36840
36841 </code></pre>
36842      * @param {Object} cfg Xtype definition of item to add.
36843      */
36844     
36845     
36846     getChildContainer: function () {
36847         return this.getEl();
36848     }
36849     
36850     
36851     /*
36852         var  ret = new Roo.factory(cfg);
36853         return ret;
36854         
36855         
36856         // add form..
36857         if (cfg.xtype.match(/^Form$/)) {
36858             
36859             var el;
36860             //if (this.footer) {
36861             //    el = this.footer.container.insertSibling(false, 'before');
36862             //} else {
36863                 el = this.el.createChild();
36864             //}
36865
36866             this.form = new  Roo.form.Form(cfg);
36867             
36868             
36869             if ( this.form.allItems.length) {
36870                 this.form.render(el.dom);
36871             }
36872             return this.form;
36873         }
36874         // should only have one of theses..
36875         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36876             // views.. should not be just added - used named prop 'view''
36877             
36878             cfg.el = this.el.appendChild(document.createElement("div"));
36879             // factory?
36880             
36881             var ret = new Roo.factory(cfg);
36882              
36883              ret.render && ret.render(false, ''); // render blank..
36884             this.view = ret;
36885             return ret;
36886         }
36887         return false;
36888     }
36889     \*/
36890 });
36891  
36892 /**
36893  * @class Roo.bootstrap.panel.Grid
36894  * @extends Roo.bootstrap.panel.Content
36895  * @constructor
36896  * Create a new GridPanel.
36897  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36898  * @param {Object} config A the config object
36899   
36900  */
36901
36902
36903
36904 Roo.bootstrap.panel.Grid = function(config)
36905 {
36906     
36907       
36908     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36909         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36910
36911     config.el = this.wrapper;
36912     //this.el = this.wrapper;
36913     
36914       if (config.container) {
36915         // ctor'ed from a Border/panel.grid
36916         
36917         
36918         this.wrapper.setStyle("overflow", "hidden");
36919         this.wrapper.addClass('roo-grid-container');
36920
36921     }
36922     
36923     
36924     if(config.toolbar){
36925         var tool_el = this.wrapper.createChild();    
36926         this.toolbar = Roo.factory(config.toolbar);
36927         var ti = [];
36928         if (config.toolbar.items) {
36929             ti = config.toolbar.items ;
36930             delete config.toolbar.items ;
36931         }
36932         
36933         var nitems = [];
36934         this.toolbar.render(tool_el);
36935         for(var i =0;i < ti.length;i++) {
36936           //  Roo.log(['add child', items[i]]);
36937             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36938         }
36939         this.toolbar.items = nitems;
36940         
36941         delete config.toolbar;
36942     }
36943     
36944     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36945     config.grid.scrollBody = true;;
36946     config.grid.monitorWindowResize = false; // turn off autosizing
36947     config.grid.autoHeight = false;
36948     config.grid.autoWidth = false;
36949     
36950     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36951     
36952     if (config.background) {
36953         // render grid on panel activation (if panel background)
36954         this.on('activate', function(gp) {
36955             if (!gp.grid.rendered) {
36956                 gp.grid.render(this.wrapper);
36957                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36958             }
36959         });
36960             
36961     } else {
36962         this.grid.render(this.wrapper);
36963         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36964
36965     }
36966     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36967     // ??? needed ??? config.el = this.wrapper;
36968     
36969     
36970     
36971   
36972     // xtype created footer. - not sure if will work as we normally have to render first..
36973     if (this.footer && !this.footer.el && this.footer.xtype) {
36974         
36975         var ctr = this.grid.getView().getFooterPanel(true);
36976         this.footer.dataSource = this.grid.dataSource;
36977         this.footer = Roo.factory(this.footer, Roo);
36978         this.footer.render(ctr);
36979         
36980     }
36981     
36982     
36983     
36984     
36985      
36986 };
36987
36988 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36989     getId : function(){
36990         return this.grid.id;
36991     },
36992     
36993     /**
36994      * Returns the grid for this panel
36995      * @return {Roo.bootstrap.Table} 
36996      */
36997     getGrid : function(){
36998         return this.grid;    
36999     },
37000     
37001     setSize : function(width, height){
37002         if(!this.ignoreResize(width, height)){
37003             var grid = this.grid;
37004             var size = this.adjustForComponents(width, height);
37005             var gridel = grid.getGridEl();
37006             gridel.setSize(size.width, size.height);
37007             /*
37008             var thd = grid.getGridEl().select('thead',true).first();
37009             var tbd = grid.getGridEl().select('tbody', true).first();
37010             if (tbd) {
37011                 tbd.setSize(width, height - thd.getHeight());
37012             }
37013             */
37014             grid.autoSize();
37015         }
37016     },
37017      
37018     
37019     
37020     beforeSlide : function(){
37021         this.grid.getView().scroller.clip();
37022     },
37023     
37024     afterSlide : function(){
37025         this.grid.getView().scroller.unclip();
37026     },
37027     
37028     destroy : function(){
37029         this.grid.destroy();
37030         delete this.grid;
37031         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37032     }
37033 });
37034
37035 /**
37036  * @class Roo.bootstrap.panel.Nest
37037  * @extends Roo.bootstrap.panel.Content
37038  * @constructor
37039  * Create a new Panel, that can contain a layout.Border.
37040  * 
37041  * 
37042  * @param {Roo.BorderLayout} layout The layout for this panel
37043  * @param {String/Object} config A string to set only the title or a config object
37044  */
37045 Roo.bootstrap.panel.Nest = function(config)
37046 {
37047     // construct with only one argument..
37048     /* FIXME - implement nicer consturctors
37049     if (layout.layout) {
37050         config = layout;
37051         layout = config.layout;
37052         delete config.layout;
37053     }
37054     if (layout.xtype && !layout.getEl) {
37055         // then layout needs constructing..
37056         layout = Roo.factory(layout, Roo);
37057     }
37058     */
37059     
37060     config.el =  config.layout.getEl();
37061     
37062     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37063     
37064     config.layout.monitorWindowResize = false; // turn off autosizing
37065     this.layout = config.layout;
37066     this.layout.getEl().addClass("roo-layout-nested-layout");
37067     
37068     
37069     
37070     
37071 };
37072
37073 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37074
37075     setSize : function(width, height){
37076         if(!this.ignoreResize(width, height)){
37077             var size = this.adjustForComponents(width, height);
37078             var el = this.layout.getEl();
37079             if (size.height < 1) {
37080                 el.setWidth(size.width);   
37081             } else {
37082                 el.setSize(size.width, size.height);
37083             }
37084             var touch = el.dom.offsetWidth;
37085             this.layout.layout();
37086             // ie requires a double layout on the first pass
37087             if(Roo.isIE && !this.initialized){
37088                 this.initialized = true;
37089                 this.layout.layout();
37090             }
37091         }
37092     },
37093     
37094     // activate all subpanels if not currently active..
37095     
37096     setActiveState : function(active){
37097         this.active = active;
37098         this.setActiveClass(active);
37099         
37100         if(!active){
37101             this.fireEvent("deactivate", this);
37102             return;
37103         }
37104         
37105         this.fireEvent("activate", this);
37106         // not sure if this should happen before or after..
37107         if (!this.layout) {
37108             return; // should not happen..
37109         }
37110         var reg = false;
37111         for (var r in this.layout.regions) {
37112             reg = this.layout.getRegion(r);
37113             if (reg.getActivePanel()) {
37114                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37115                 reg.setActivePanel(reg.getActivePanel());
37116                 continue;
37117             }
37118             if (!reg.panels.length) {
37119                 continue;
37120             }
37121             reg.showPanel(reg.getPanel(0));
37122         }
37123         
37124         
37125         
37126         
37127     },
37128     
37129     /**
37130      * Returns the nested BorderLayout for this panel
37131      * @return {Roo.BorderLayout} 
37132      */
37133     getLayout : function(){
37134         return this.layout;
37135     },
37136     
37137      /**
37138      * Adds a xtype elements to the layout of the nested panel
37139      * <pre><code>
37140
37141 panel.addxtype({
37142        xtype : 'ContentPanel',
37143        region: 'west',
37144        items: [ .... ]
37145    }
37146 );
37147
37148 panel.addxtype({
37149         xtype : 'NestedLayoutPanel',
37150         region: 'west',
37151         layout: {
37152            center: { },
37153            west: { }   
37154         },
37155         items : [ ... list of content panels or nested layout panels.. ]
37156    }
37157 );
37158 </code></pre>
37159      * @param {Object} cfg Xtype definition of item to add.
37160      */
37161     addxtype : function(cfg) {
37162         return this.layout.addxtype(cfg);
37163     
37164     }
37165 });        /*
37166  * Based on:
37167  * Ext JS Library 1.1.1
37168  * Copyright(c) 2006-2007, Ext JS, LLC.
37169  *
37170  * Originally Released Under LGPL - original licence link has changed is not relivant.
37171  *
37172  * Fork - LGPL
37173  * <script type="text/javascript">
37174  */
37175 /**
37176  * @class Roo.TabPanel
37177  * @extends Roo.util.Observable
37178  * A lightweight tab container.
37179  * <br><br>
37180  * Usage:
37181  * <pre><code>
37182 // basic tabs 1, built from existing content
37183 var tabs = new Roo.TabPanel("tabs1");
37184 tabs.addTab("script", "View Script");
37185 tabs.addTab("markup", "View Markup");
37186 tabs.activate("script");
37187
37188 // more advanced tabs, built from javascript
37189 var jtabs = new Roo.TabPanel("jtabs");
37190 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37191
37192 // set up the UpdateManager
37193 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37194 var updater = tab2.getUpdateManager();
37195 updater.setDefaultUrl("ajax1.htm");
37196 tab2.on('activate', updater.refresh, updater, true);
37197
37198 // Use setUrl for Ajax loading
37199 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37200 tab3.setUrl("ajax2.htm", null, true);
37201
37202 // Disabled tab
37203 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37204 tab4.disable();
37205
37206 jtabs.activate("jtabs-1");
37207  * </code></pre>
37208  * @constructor
37209  * Create a new TabPanel.
37210  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37211  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37212  */
37213 Roo.bootstrap.panel.Tabs = function(config){
37214     /**
37215     * The container element for this TabPanel.
37216     * @type Roo.Element
37217     */
37218     this.el = Roo.get(config.el);
37219     delete config.el;
37220     if(config){
37221         if(typeof config == "boolean"){
37222             this.tabPosition = config ? "bottom" : "top";
37223         }else{
37224             Roo.apply(this, config);
37225         }
37226     }
37227     
37228     if(this.tabPosition == "bottom"){
37229         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37230         this.el.addClass("roo-tabs-bottom");
37231     }
37232     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37233     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37234     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37235     if(Roo.isIE){
37236         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37237     }
37238     if(this.tabPosition != "bottom"){
37239         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37240          * @type Roo.Element
37241          */
37242         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37243         this.el.addClass("roo-tabs-top");
37244     }
37245     this.items = [];
37246
37247     this.bodyEl.setStyle("position", "relative");
37248
37249     this.active = null;
37250     this.activateDelegate = this.activate.createDelegate(this);
37251
37252     this.addEvents({
37253         /**
37254          * @event tabchange
37255          * Fires when the active tab changes
37256          * @param {Roo.TabPanel} this
37257          * @param {Roo.TabPanelItem} activePanel The new active tab
37258          */
37259         "tabchange": true,
37260         /**
37261          * @event beforetabchange
37262          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37263          * @param {Roo.TabPanel} this
37264          * @param {Object} e Set cancel to true on this object to cancel the tab change
37265          * @param {Roo.TabPanelItem} tab The tab being changed to
37266          */
37267         "beforetabchange" : true
37268     });
37269
37270     Roo.EventManager.onWindowResize(this.onResize, this);
37271     this.cpad = this.el.getPadding("lr");
37272     this.hiddenCount = 0;
37273
37274
37275     // toolbar on the tabbar support...
37276     if (this.toolbar) {
37277         alert("no toolbar support yet");
37278         this.toolbar  = false;
37279         /*
37280         var tcfg = this.toolbar;
37281         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37282         this.toolbar = new Roo.Toolbar(tcfg);
37283         if (Roo.isSafari) {
37284             var tbl = tcfg.container.child('table', true);
37285             tbl.setAttribute('width', '100%');
37286         }
37287         */
37288         
37289     }
37290    
37291
37292
37293     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37294 };
37295
37296 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37297     /*
37298      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37299      */
37300     tabPosition : "top",
37301     /*
37302      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37303      */
37304     currentTabWidth : 0,
37305     /*
37306      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37307      */
37308     minTabWidth : 40,
37309     /*
37310      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37311      */
37312     maxTabWidth : 250,
37313     /*
37314      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37315      */
37316     preferredTabWidth : 175,
37317     /*
37318      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37319      */
37320     resizeTabs : false,
37321     /*
37322      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37323      */
37324     monitorResize : true,
37325     /*
37326      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37327      */
37328     toolbar : false,
37329
37330     /**
37331      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37332      * @param {String} id The id of the div to use <b>or create</b>
37333      * @param {String} text The text for the tab
37334      * @param {String} content (optional) Content to put in the TabPanelItem body
37335      * @param {Boolean} closable (optional) True to create a close icon on the tab
37336      * @return {Roo.TabPanelItem} The created TabPanelItem
37337      */
37338     addTab : function(id, text, content, closable, tpl)
37339     {
37340         var item = new Roo.bootstrap.panel.TabItem({
37341             panel: this,
37342             id : id,
37343             text : text,
37344             closable : closable,
37345             tpl : tpl
37346         });
37347         this.addTabItem(item);
37348         if(content){
37349             item.setContent(content);
37350         }
37351         return item;
37352     },
37353
37354     /**
37355      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37356      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37357      * @return {Roo.TabPanelItem}
37358      */
37359     getTab : function(id){
37360         return this.items[id];
37361     },
37362
37363     /**
37364      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37365      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37366      */
37367     hideTab : function(id){
37368         var t = this.items[id];
37369         if(!t.isHidden()){
37370            t.setHidden(true);
37371            this.hiddenCount++;
37372            this.autoSizeTabs();
37373         }
37374     },
37375
37376     /**
37377      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37378      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37379      */
37380     unhideTab : function(id){
37381         var t = this.items[id];
37382         if(t.isHidden()){
37383            t.setHidden(false);
37384            this.hiddenCount--;
37385            this.autoSizeTabs();
37386         }
37387     },
37388
37389     /**
37390      * Adds an existing {@link Roo.TabPanelItem}.
37391      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37392      */
37393     addTabItem : function(item){
37394         this.items[item.id] = item;
37395         this.items.push(item);
37396       //  if(this.resizeTabs){
37397     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37398   //         this.autoSizeTabs();
37399 //        }else{
37400 //            item.autoSize();
37401        // }
37402     },
37403
37404     /**
37405      * Removes a {@link Roo.TabPanelItem}.
37406      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37407      */
37408     removeTab : function(id){
37409         var items = this.items;
37410         var tab = items[id];
37411         if(!tab) { return; }
37412         var index = items.indexOf(tab);
37413         if(this.active == tab && items.length > 1){
37414             var newTab = this.getNextAvailable(index);
37415             if(newTab) {
37416                 newTab.activate();
37417             }
37418         }
37419         this.stripEl.dom.removeChild(tab.pnode.dom);
37420         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37421             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37422         }
37423         items.splice(index, 1);
37424         delete this.items[tab.id];
37425         tab.fireEvent("close", tab);
37426         tab.purgeListeners();
37427         this.autoSizeTabs();
37428     },
37429
37430     getNextAvailable : function(start){
37431         var items = this.items;
37432         var index = start;
37433         // look for a next tab that will slide over to
37434         // replace the one being removed
37435         while(index < items.length){
37436             var item = items[++index];
37437             if(item && !item.isHidden()){
37438                 return item;
37439             }
37440         }
37441         // if one isn't found select the previous tab (on the left)
37442         index = start;
37443         while(index >= 0){
37444             var item = items[--index];
37445             if(item && !item.isHidden()){
37446                 return item;
37447             }
37448         }
37449         return null;
37450     },
37451
37452     /**
37453      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37454      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37455      */
37456     disableTab : function(id){
37457         var tab = this.items[id];
37458         if(tab && this.active != tab){
37459             tab.disable();
37460         }
37461     },
37462
37463     /**
37464      * Enables a {@link Roo.TabPanelItem} that is disabled.
37465      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37466      */
37467     enableTab : function(id){
37468         var tab = this.items[id];
37469         tab.enable();
37470     },
37471
37472     /**
37473      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37474      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37475      * @return {Roo.TabPanelItem} The TabPanelItem.
37476      */
37477     activate : function(id){
37478         var tab = this.items[id];
37479         if(!tab){
37480             return null;
37481         }
37482         if(tab == this.active || tab.disabled){
37483             return tab;
37484         }
37485         var e = {};
37486         this.fireEvent("beforetabchange", this, e, tab);
37487         if(e.cancel !== true && !tab.disabled){
37488             if(this.active){
37489                 this.active.hide();
37490             }
37491             this.active = this.items[id];
37492             this.active.show();
37493             this.fireEvent("tabchange", this, this.active);
37494         }
37495         return tab;
37496     },
37497
37498     /**
37499      * Gets the active {@link Roo.TabPanelItem}.
37500      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37501      */
37502     getActiveTab : function(){
37503         return this.active;
37504     },
37505
37506     /**
37507      * Updates the tab body element to fit the height of the container element
37508      * for overflow scrolling
37509      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37510      */
37511     syncHeight : function(targetHeight){
37512         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37513         var bm = this.bodyEl.getMargins();
37514         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37515         this.bodyEl.setHeight(newHeight);
37516         return newHeight;
37517     },
37518
37519     onResize : function(){
37520         if(this.monitorResize){
37521             this.autoSizeTabs();
37522         }
37523     },
37524
37525     /**
37526      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37527      */
37528     beginUpdate : function(){
37529         this.updating = true;
37530     },
37531
37532     /**
37533      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37534      */
37535     endUpdate : function(){
37536         this.updating = false;
37537         this.autoSizeTabs();
37538     },
37539
37540     /**
37541      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37542      */
37543     autoSizeTabs : function(){
37544         var count = this.items.length;
37545         var vcount = count - this.hiddenCount;
37546         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37547             return;
37548         }
37549         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37550         var availWidth = Math.floor(w / vcount);
37551         var b = this.stripBody;
37552         if(b.getWidth() > w){
37553             var tabs = this.items;
37554             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37555             if(availWidth < this.minTabWidth){
37556                 /*if(!this.sleft){    // incomplete scrolling code
37557                     this.createScrollButtons();
37558                 }
37559                 this.showScroll();
37560                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37561             }
37562         }else{
37563             if(this.currentTabWidth < this.preferredTabWidth){
37564                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37565             }
37566         }
37567     },
37568
37569     /**
37570      * Returns the number of tabs in this TabPanel.
37571      * @return {Number}
37572      */
37573      getCount : function(){
37574          return this.items.length;
37575      },
37576
37577     /**
37578      * Resizes all the tabs to the passed width
37579      * @param {Number} The new width
37580      */
37581     setTabWidth : function(width){
37582         this.currentTabWidth = width;
37583         for(var i = 0, len = this.items.length; i < len; i++) {
37584                 if(!this.items[i].isHidden()) {
37585                 this.items[i].setWidth(width);
37586             }
37587         }
37588     },
37589
37590     /**
37591      * Destroys this TabPanel
37592      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37593      */
37594     destroy : function(removeEl){
37595         Roo.EventManager.removeResizeListener(this.onResize, this);
37596         for(var i = 0, len = this.items.length; i < len; i++){
37597             this.items[i].purgeListeners();
37598         }
37599         if(removeEl === true){
37600             this.el.update("");
37601             this.el.remove();
37602         }
37603     },
37604     
37605     createStrip : function(container)
37606     {
37607         var strip = document.createElement("nav");
37608         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37609         container.appendChild(strip);
37610         return strip;
37611     },
37612     
37613     createStripList : function(strip)
37614     {
37615         // div wrapper for retard IE
37616         // returns the "tr" element.
37617         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37618         //'<div class="x-tabs-strip-wrap">'+
37619           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37620           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37621         return strip.firstChild; //.firstChild.firstChild.firstChild;
37622     },
37623     createBody : function(container)
37624     {
37625         var body = document.createElement("div");
37626         Roo.id(body, "tab-body");
37627         //Roo.fly(body).addClass("x-tabs-body");
37628         Roo.fly(body).addClass("tab-content");
37629         container.appendChild(body);
37630         return body;
37631     },
37632     createItemBody :function(bodyEl, id){
37633         var body = Roo.getDom(id);
37634         if(!body){
37635             body = document.createElement("div");
37636             body.id = id;
37637         }
37638         //Roo.fly(body).addClass("x-tabs-item-body");
37639         Roo.fly(body).addClass("tab-pane");
37640          bodyEl.insertBefore(body, bodyEl.firstChild);
37641         return body;
37642     },
37643     /** @private */
37644     createStripElements :  function(stripEl, text, closable, tpl)
37645     {
37646         var td = document.createElement("li"); // was td..
37647         
37648         
37649         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37650         
37651         
37652         stripEl.appendChild(td);
37653         /*if(closable){
37654             td.className = "x-tabs-closable";
37655             if(!this.closeTpl){
37656                 this.closeTpl = new Roo.Template(
37657                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37658                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37659                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37660                 );
37661             }
37662             var el = this.closeTpl.overwrite(td, {"text": text});
37663             var close = el.getElementsByTagName("div")[0];
37664             var inner = el.getElementsByTagName("em")[0];
37665             return {"el": el, "close": close, "inner": inner};
37666         } else {
37667         */
37668         // not sure what this is..
37669 //            if(!this.tabTpl){
37670                 //this.tabTpl = new Roo.Template(
37671                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37672                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37673                 //);
37674 //                this.tabTpl = new Roo.Template(
37675 //                   '<a href="#">' +
37676 //                   '<span unselectable="on"' +
37677 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37678 //                            ' >{text}</span></a>'
37679 //                );
37680 //                
37681 //            }
37682
37683
37684             var template = tpl || this.tabTpl || false;
37685             
37686             if(!template){
37687                 
37688                 template = new Roo.Template(
37689                    '<a href="#">' +
37690                    '<span unselectable="on"' +
37691                             (this.disableTooltips ? '' : ' title="{text}"') +
37692                             ' >{text}</span></a>'
37693                 );
37694             }
37695             
37696             switch (typeof(template)) {
37697                 case 'object' :
37698                     break;
37699                 case 'string' :
37700                     template = new Roo.Template(template);
37701                     break;
37702                 default :
37703                     break;
37704             }
37705             
37706             var el = template.overwrite(td, {"text": text});
37707             
37708             var inner = el.getElementsByTagName("span")[0];
37709             
37710             return {"el": el, "inner": inner};
37711             
37712     }
37713         
37714     
37715 });
37716
37717 /**
37718  * @class Roo.TabPanelItem
37719  * @extends Roo.util.Observable
37720  * Represents an individual item (tab plus body) in a TabPanel.
37721  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37722  * @param {String} id The id of this TabPanelItem
37723  * @param {String} text The text for the tab of this TabPanelItem
37724  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37725  */
37726 Roo.bootstrap.panel.TabItem = function(config){
37727     /**
37728      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37729      * @type Roo.TabPanel
37730      */
37731     this.tabPanel = config.panel;
37732     /**
37733      * The id for this TabPanelItem
37734      * @type String
37735      */
37736     this.id = config.id;
37737     /** @private */
37738     this.disabled = false;
37739     /** @private */
37740     this.text = config.text;
37741     /** @private */
37742     this.loaded = false;
37743     this.closable = config.closable;
37744
37745     /**
37746      * The body element for this TabPanelItem.
37747      * @type Roo.Element
37748      */
37749     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37750     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37751     this.bodyEl.setStyle("display", "block");
37752     this.bodyEl.setStyle("zoom", "1");
37753     //this.hideAction();
37754
37755     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37756     /** @private */
37757     this.el = Roo.get(els.el);
37758     this.inner = Roo.get(els.inner, true);
37759     this.textEl = Roo.get(this.el.dom.firstChild, true);
37760     this.pnode = Roo.get(els.el.parentNode, true);
37761 //    this.el.on("mousedown", this.onTabMouseDown, this);
37762     this.el.on("click", this.onTabClick, this);
37763     /** @private */
37764     if(config.closable){
37765         var c = Roo.get(els.close, true);
37766         c.dom.title = this.closeText;
37767         c.addClassOnOver("close-over");
37768         c.on("click", this.closeClick, this);
37769      }
37770
37771     this.addEvents({
37772          /**
37773          * @event activate
37774          * Fires when this tab becomes the active tab.
37775          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37776          * @param {Roo.TabPanelItem} this
37777          */
37778         "activate": true,
37779         /**
37780          * @event beforeclose
37781          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37782          * @param {Roo.TabPanelItem} this
37783          * @param {Object} e Set cancel to true on this object to cancel the close.
37784          */
37785         "beforeclose": true,
37786         /**
37787          * @event close
37788          * Fires when this tab is closed.
37789          * @param {Roo.TabPanelItem} this
37790          */
37791          "close": true,
37792         /**
37793          * @event deactivate
37794          * Fires when this tab is no longer the active tab.
37795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37796          * @param {Roo.TabPanelItem} this
37797          */
37798          "deactivate" : true
37799     });
37800     this.hidden = false;
37801
37802     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37803 };
37804
37805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37806            {
37807     purgeListeners : function(){
37808        Roo.util.Observable.prototype.purgeListeners.call(this);
37809        this.el.removeAllListeners();
37810     },
37811     /**
37812      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37813      */
37814     show : function(){
37815         this.pnode.addClass("active");
37816         this.showAction();
37817         if(Roo.isOpera){
37818             this.tabPanel.stripWrap.repaint();
37819         }
37820         this.fireEvent("activate", this.tabPanel, this);
37821     },
37822
37823     /**
37824      * Returns true if this tab is the active tab.
37825      * @return {Boolean}
37826      */
37827     isActive : function(){
37828         return this.tabPanel.getActiveTab() == this;
37829     },
37830
37831     /**
37832      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37833      */
37834     hide : function(){
37835         this.pnode.removeClass("active");
37836         this.hideAction();
37837         this.fireEvent("deactivate", this.tabPanel, this);
37838     },
37839
37840     hideAction : function(){
37841         this.bodyEl.hide();
37842         this.bodyEl.setStyle("position", "absolute");
37843         this.bodyEl.setLeft("-20000px");
37844         this.bodyEl.setTop("-20000px");
37845     },
37846
37847     showAction : function(){
37848         this.bodyEl.setStyle("position", "relative");
37849         this.bodyEl.setTop("");
37850         this.bodyEl.setLeft("");
37851         this.bodyEl.show();
37852     },
37853
37854     /**
37855      * Set the tooltip for the tab.
37856      * @param {String} tooltip The tab's tooltip
37857      */
37858     setTooltip : function(text){
37859         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37860             this.textEl.dom.qtip = text;
37861             this.textEl.dom.removeAttribute('title');
37862         }else{
37863             this.textEl.dom.title = text;
37864         }
37865     },
37866
37867     onTabClick : function(e){
37868         e.preventDefault();
37869         this.tabPanel.activate(this.id);
37870     },
37871
37872     onTabMouseDown : function(e){
37873         e.preventDefault();
37874         this.tabPanel.activate(this.id);
37875     },
37876 /*
37877     getWidth : function(){
37878         return this.inner.getWidth();
37879     },
37880
37881     setWidth : function(width){
37882         var iwidth = width - this.pnode.getPadding("lr");
37883         this.inner.setWidth(iwidth);
37884         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37885         this.pnode.setWidth(width);
37886     },
37887 */
37888     /**
37889      * Show or hide the tab
37890      * @param {Boolean} hidden True to hide or false to show.
37891      */
37892     setHidden : function(hidden){
37893         this.hidden = hidden;
37894         this.pnode.setStyle("display", hidden ? "none" : "");
37895     },
37896
37897     /**
37898      * Returns true if this tab is "hidden"
37899      * @return {Boolean}
37900      */
37901     isHidden : function(){
37902         return this.hidden;
37903     },
37904
37905     /**
37906      * Returns the text for this tab
37907      * @return {String}
37908      */
37909     getText : function(){
37910         return this.text;
37911     },
37912     /*
37913     autoSize : function(){
37914         //this.el.beginMeasure();
37915         this.textEl.setWidth(1);
37916         /*
37917          *  #2804 [new] Tabs in Roojs
37918          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37919          */
37920         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37921         //this.el.endMeasure();
37922     //},
37923
37924     /**
37925      * Sets the text for the tab (Note: this also sets the tooltip text)
37926      * @param {String} text The tab's text and tooltip
37927      */
37928     setText : function(text){
37929         this.text = text;
37930         this.textEl.update(text);
37931         this.setTooltip(text);
37932         //if(!this.tabPanel.resizeTabs){
37933         //    this.autoSize();
37934         //}
37935     },
37936     /**
37937      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37938      */
37939     activate : function(){
37940         this.tabPanel.activate(this.id);
37941     },
37942
37943     /**
37944      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37945      */
37946     disable : function(){
37947         if(this.tabPanel.active != this){
37948             this.disabled = true;
37949             this.pnode.addClass("disabled");
37950         }
37951     },
37952
37953     /**
37954      * Enables this TabPanelItem if it was previously disabled.
37955      */
37956     enable : function(){
37957         this.disabled = false;
37958         this.pnode.removeClass("disabled");
37959     },
37960
37961     /**
37962      * Sets the content for this TabPanelItem.
37963      * @param {String} content The content
37964      * @param {Boolean} loadScripts true to look for and load scripts
37965      */
37966     setContent : function(content, loadScripts){
37967         this.bodyEl.update(content, loadScripts);
37968     },
37969
37970     /**
37971      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37972      * @return {Roo.UpdateManager} The UpdateManager
37973      */
37974     getUpdateManager : function(){
37975         return this.bodyEl.getUpdateManager();
37976     },
37977
37978     /**
37979      * Set a URL to be used to load the content for this TabPanelItem.
37980      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37981      * @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)
37982      * @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)
37983      * @return {Roo.UpdateManager} The UpdateManager
37984      */
37985     setUrl : function(url, params, loadOnce){
37986         if(this.refreshDelegate){
37987             this.un('activate', this.refreshDelegate);
37988         }
37989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37990         this.on("activate", this.refreshDelegate);
37991         return this.bodyEl.getUpdateManager();
37992     },
37993
37994     /** @private */
37995     _handleRefresh : function(url, params, loadOnce){
37996         if(!loadOnce || !this.loaded){
37997             var updater = this.bodyEl.getUpdateManager();
37998             updater.update(url, params, this._setLoaded.createDelegate(this));
37999         }
38000     },
38001
38002     /**
38003      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38004      *   Will fail silently if the setUrl method has not been called.
38005      *   This does not activate the panel, just updates its content.
38006      */
38007     refresh : function(){
38008         if(this.refreshDelegate){
38009            this.loaded = false;
38010            this.refreshDelegate();
38011         }
38012     },
38013
38014     /** @private */
38015     _setLoaded : function(){
38016         this.loaded = true;
38017     },
38018
38019     /** @private */
38020     closeClick : function(e){
38021         var o = {};
38022         e.stopEvent();
38023         this.fireEvent("beforeclose", this, o);
38024         if(o.cancel !== true){
38025             this.tabPanel.removeTab(this.id);
38026         }
38027     },
38028     /**
38029      * The text displayed in the tooltip for the close icon.
38030      * @type String
38031      */
38032     closeText : "Close this tab"
38033 });
38034 /**
38035 *    This script refer to:
38036 *    Title: International Telephone Input
38037 *    Author: Jack O'Connor
38038 *    Code version:  v12.1.12
38039 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38040 **/
38041
38042 Roo.bootstrap.PhoneInputData = function() {
38043     var d = [
38044       [
38045         "Afghanistan (‫افغانستان‬‎)",
38046         "af",
38047         "93"
38048       ],
38049       [
38050         "Albania (Shqipëri)",
38051         "al",
38052         "355"
38053       ],
38054       [
38055         "Algeria (‫الجزائر‬‎)",
38056         "dz",
38057         "213"
38058       ],
38059       [
38060         "American Samoa",
38061         "as",
38062         "1684"
38063       ],
38064       [
38065         "Andorra",
38066         "ad",
38067         "376"
38068       ],
38069       [
38070         "Angola",
38071         "ao",
38072         "244"
38073       ],
38074       [
38075         "Anguilla",
38076         "ai",
38077         "1264"
38078       ],
38079       [
38080         "Antigua and Barbuda",
38081         "ag",
38082         "1268"
38083       ],
38084       [
38085         "Argentina",
38086         "ar",
38087         "54"
38088       ],
38089       [
38090         "Armenia (Հայաստան)",
38091         "am",
38092         "374"
38093       ],
38094       [
38095         "Aruba",
38096         "aw",
38097         "297"
38098       ],
38099       [
38100         "Australia",
38101         "au",
38102         "61",
38103         0
38104       ],
38105       [
38106         "Austria (Österreich)",
38107         "at",
38108         "43"
38109       ],
38110       [
38111         "Azerbaijan (Azərbaycan)",
38112         "az",
38113         "994"
38114       ],
38115       [
38116         "Bahamas",
38117         "bs",
38118         "1242"
38119       ],
38120       [
38121         "Bahrain (‫البحرين‬‎)",
38122         "bh",
38123         "973"
38124       ],
38125       [
38126         "Bangladesh (বাংলাদেশ)",
38127         "bd",
38128         "880"
38129       ],
38130       [
38131         "Barbados",
38132         "bb",
38133         "1246"
38134       ],
38135       [
38136         "Belarus (Беларусь)",
38137         "by",
38138         "375"
38139       ],
38140       [
38141         "Belgium (België)",
38142         "be",
38143         "32"
38144       ],
38145       [
38146         "Belize",
38147         "bz",
38148         "501"
38149       ],
38150       [
38151         "Benin (Bénin)",
38152         "bj",
38153         "229"
38154       ],
38155       [
38156         "Bermuda",
38157         "bm",
38158         "1441"
38159       ],
38160       [
38161         "Bhutan (འབྲུག)",
38162         "bt",
38163         "975"
38164       ],
38165       [
38166         "Bolivia",
38167         "bo",
38168         "591"
38169       ],
38170       [
38171         "Bosnia and Herzegovina (Босна и Херцеговина)",
38172         "ba",
38173         "387"
38174       ],
38175       [
38176         "Botswana",
38177         "bw",
38178         "267"
38179       ],
38180       [
38181         "Brazil (Brasil)",
38182         "br",
38183         "55"
38184       ],
38185       [
38186         "British Indian Ocean Territory",
38187         "io",
38188         "246"
38189       ],
38190       [
38191         "British Virgin Islands",
38192         "vg",
38193         "1284"
38194       ],
38195       [
38196         "Brunei",
38197         "bn",
38198         "673"
38199       ],
38200       [
38201         "Bulgaria (България)",
38202         "bg",
38203         "359"
38204       ],
38205       [
38206         "Burkina Faso",
38207         "bf",
38208         "226"
38209       ],
38210       [
38211         "Burundi (Uburundi)",
38212         "bi",
38213         "257"
38214       ],
38215       [
38216         "Cambodia (កម្ពុជា)",
38217         "kh",
38218         "855"
38219       ],
38220       [
38221         "Cameroon (Cameroun)",
38222         "cm",
38223         "237"
38224       ],
38225       [
38226         "Canada",
38227         "ca",
38228         "1",
38229         1,
38230         ["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"]
38231       ],
38232       [
38233         "Cape Verde (Kabu Verdi)",
38234         "cv",
38235         "238"
38236       ],
38237       [
38238         "Caribbean Netherlands",
38239         "bq",
38240         "599",
38241         1
38242       ],
38243       [
38244         "Cayman Islands",
38245         "ky",
38246         "1345"
38247       ],
38248       [
38249         "Central African Republic (République centrafricaine)",
38250         "cf",
38251         "236"
38252       ],
38253       [
38254         "Chad (Tchad)",
38255         "td",
38256         "235"
38257       ],
38258       [
38259         "Chile",
38260         "cl",
38261         "56"
38262       ],
38263       [
38264         "China (中国)",
38265         "cn",
38266         "86"
38267       ],
38268       [
38269         "Christmas Island",
38270         "cx",
38271         "61",
38272         2
38273       ],
38274       [
38275         "Cocos (Keeling) Islands",
38276         "cc",
38277         "61",
38278         1
38279       ],
38280       [
38281         "Colombia",
38282         "co",
38283         "57"
38284       ],
38285       [
38286         "Comoros (‫جزر القمر‬‎)",
38287         "km",
38288         "269"
38289       ],
38290       [
38291         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38292         "cd",
38293         "243"
38294       ],
38295       [
38296         "Congo (Republic) (Congo-Brazzaville)",
38297         "cg",
38298         "242"
38299       ],
38300       [
38301         "Cook Islands",
38302         "ck",
38303         "682"
38304       ],
38305       [
38306         "Costa Rica",
38307         "cr",
38308         "506"
38309       ],
38310       [
38311         "Côte d’Ivoire",
38312         "ci",
38313         "225"
38314       ],
38315       [
38316         "Croatia (Hrvatska)",
38317         "hr",
38318         "385"
38319       ],
38320       [
38321         "Cuba",
38322         "cu",
38323         "53"
38324       ],
38325       [
38326         "Curaçao",
38327         "cw",
38328         "599",
38329         0
38330       ],
38331       [
38332         "Cyprus (Κύπρος)",
38333         "cy",
38334         "357"
38335       ],
38336       [
38337         "Czech Republic (Česká republika)",
38338         "cz",
38339         "420"
38340       ],
38341       [
38342         "Denmark (Danmark)",
38343         "dk",
38344         "45"
38345       ],
38346       [
38347         "Djibouti",
38348         "dj",
38349         "253"
38350       ],
38351       [
38352         "Dominica",
38353         "dm",
38354         "1767"
38355       ],
38356       [
38357         "Dominican Republic (República Dominicana)",
38358         "do",
38359         "1",
38360         2,
38361         ["809", "829", "849"]
38362       ],
38363       [
38364         "Ecuador",
38365         "ec",
38366         "593"
38367       ],
38368       [
38369         "Egypt (‫مصر‬‎)",
38370         "eg",
38371         "20"
38372       ],
38373       [
38374         "El Salvador",
38375         "sv",
38376         "503"
38377       ],
38378       [
38379         "Equatorial Guinea (Guinea Ecuatorial)",
38380         "gq",
38381         "240"
38382       ],
38383       [
38384         "Eritrea",
38385         "er",
38386         "291"
38387       ],
38388       [
38389         "Estonia (Eesti)",
38390         "ee",
38391         "372"
38392       ],
38393       [
38394         "Ethiopia",
38395         "et",
38396         "251"
38397       ],
38398       [
38399         "Falkland Islands (Islas Malvinas)",
38400         "fk",
38401         "500"
38402       ],
38403       [
38404         "Faroe Islands (Føroyar)",
38405         "fo",
38406         "298"
38407       ],
38408       [
38409         "Fiji",
38410         "fj",
38411         "679"
38412       ],
38413       [
38414         "Finland (Suomi)",
38415         "fi",
38416         "358",
38417         0
38418       ],
38419       [
38420         "France",
38421         "fr",
38422         "33"
38423       ],
38424       [
38425         "French Guiana (Guyane française)",
38426         "gf",
38427         "594"
38428       ],
38429       [
38430         "French Polynesia (Polynésie française)",
38431         "pf",
38432         "689"
38433       ],
38434       [
38435         "Gabon",
38436         "ga",
38437         "241"
38438       ],
38439       [
38440         "Gambia",
38441         "gm",
38442         "220"
38443       ],
38444       [
38445         "Georgia (საქართველო)",
38446         "ge",
38447         "995"
38448       ],
38449       [
38450         "Germany (Deutschland)",
38451         "de",
38452         "49"
38453       ],
38454       [
38455         "Ghana (Gaana)",
38456         "gh",
38457         "233"
38458       ],
38459       [
38460         "Gibraltar",
38461         "gi",
38462         "350"
38463       ],
38464       [
38465         "Greece (Ελλάδα)",
38466         "gr",
38467         "30"
38468       ],
38469       [
38470         "Greenland (Kalaallit Nunaat)",
38471         "gl",
38472         "299"
38473       ],
38474       [
38475         "Grenada",
38476         "gd",
38477         "1473"
38478       ],
38479       [
38480         "Guadeloupe",
38481         "gp",
38482         "590",
38483         0
38484       ],
38485       [
38486         "Guam",
38487         "gu",
38488         "1671"
38489       ],
38490       [
38491         "Guatemala",
38492         "gt",
38493         "502"
38494       ],
38495       [
38496         "Guernsey",
38497         "gg",
38498         "44",
38499         1
38500       ],
38501       [
38502         "Guinea (Guinée)",
38503         "gn",
38504         "224"
38505       ],
38506       [
38507         "Guinea-Bissau (Guiné Bissau)",
38508         "gw",
38509         "245"
38510       ],
38511       [
38512         "Guyana",
38513         "gy",
38514         "592"
38515       ],
38516       [
38517         "Haiti",
38518         "ht",
38519         "509"
38520       ],
38521       [
38522         "Honduras",
38523         "hn",
38524         "504"
38525       ],
38526       [
38527         "Hong Kong (香港)",
38528         "hk",
38529         "852"
38530       ],
38531       [
38532         "Hungary (Magyarország)",
38533         "hu",
38534         "36"
38535       ],
38536       [
38537         "Iceland (Ísland)",
38538         "is",
38539         "354"
38540       ],
38541       [
38542         "India (भारत)",
38543         "in",
38544         "91"
38545       ],
38546       [
38547         "Indonesia",
38548         "id",
38549         "62"
38550       ],
38551       [
38552         "Iran (‫ایران‬‎)",
38553         "ir",
38554         "98"
38555       ],
38556       [
38557         "Iraq (‫العراق‬‎)",
38558         "iq",
38559         "964"
38560       ],
38561       [
38562         "Ireland",
38563         "ie",
38564         "353"
38565       ],
38566       [
38567         "Isle of Man",
38568         "im",
38569         "44",
38570         2
38571       ],
38572       [
38573         "Israel (‫ישראל‬‎)",
38574         "il",
38575         "972"
38576       ],
38577       [
38578         "Italy (Italia)",
38579         "it",
38580         "39",
38581         0
38582       ],
38583       [
38584         "Jamaica",
38585         "jm",
38586         "1876"
38587       ],
38588       [
38589         "Japan (日本)",
38590         "jp",
38591         "81"
38592       ],
38593       [
38594         "Jersey",
38595         "je",
38596         "44",
38597         3
38598       ],
38599       [
38600         "Jordan (‫الأردن‬‎)",
38601         "jo",
38602         "962"
38603       ],
38604       [
38605         "Kazakhstan (Казахстан)",
38606         "kz",
38607         "7",
38608         1
38609       ],
38610       [
38611         "Kenya",
38612         "ke",
38613         "254"
38614       ],
38615       [
38616         "Kiribati",
38617         "ki",
38618         "686"
38619       ],
38620       [
38621         "Kosovo",
38622         "xk",
38623         "383"
38624       ],
38625       [
38626         "Kuwait (‫الكويت‬‎)",
38627         "kw",
38628         "965"
38629       ],
38630       [
38631         "Kyrgyzstan (Кыргызстан)",
38632         "kg",
38633         "996"
38634       ],
38635       [
38636         "Laos (ລາວ)",
38637         "la",
38638         "856"
38639       ],
38640       [
38641         "Latvia (Latvija)",
38642         "lv",
38643         "371"
38644       ],
38645       [
38646         "Lebanon (‫لبنان‬‎)",
38647         "lb",
38648         "961"
38649       ],
38650       [
38651         "Lesotho",
38652         "ls",
38653         "266"
38654       ],
38655       [
38656         "Liberia",
38657         "lr",
38658         "231"
38659       ],
38660       [
38661         "Libya (‫ليبيا‬‎)",
38662         "ly",
38663         "218"
38664       ],
38665       [
38666         "Liechtenstein",
38667         "li",
38668         "423"
38669       ],
38670       [
38671         "Lithuania (Lietuva)",
38672         "lt",
38673         "370"
38674       ],
38675       [
38676         "Luxembourg",
38677         "lu",
38678         "352"
38679       ],
38680       [
38681         "Macau (澳門)",
38682         "mo",
38683         "853"
38684       ],
38685       [
38686         "Macedonia (FYROM) (Македонија)",
38687         "mk",
38688         "389"
38689       ],
38690       [
38691         "Madagascar (Madagasikara)",
38692         "mg",
38693         "261"
38694       ],
38695       [
38696         "Malawi",
38697         "mw",
38698         "265"
38699       ],
38700       [
38701         "Malaysia",
38702         "my",
38703         "60"
38704       ],
38705       [
38706         "Maldives",
38707         "mv",
38708         "960"
38709       ],
38710       [
38711         "Mali",
38712         "ml",
38713         "223"
38714       ],
38715       [
38716         "Malta",
38717         "mt",
38718         "356"
38719       ],
38720       [
38721         "Marshall Islands",
38722         "mh",
38723         "692"
38724       ],
38725       [
38726         "Martinique",
38727         "mq",
38728         "596"
38729       ],
38730       [
38731         "Mauritania (‫موريتانيا‬‎)",
38732         "mr",
38733         "222"
38734       ],
38735       [
38736         "Mauritius (Moris)",
38737         "mu",
38738         "230"
38739       ],
38740       [
38741         "Mayotte",
38742         "yt",
38743         "262",
38744         1
38745       ],
38746       [
38747         "Mexico (México)",
38748         "mx",
38749         "52"
38750       ],
38751       [
38752         "Micronesia",
38753         "fm",
38754         "691"
38755       ],
38756       [
38757         "Moldova (Republica Moldova)",
38758         "md",
38759         "373"
38760       ],
38761       [
38762         "Monaco",
38763         "mc",
38764         "377"
38765       ],
38766       [
38767         "Mongolia (Монгол)",
38768         "mn",
38769         "976"
38770       ],
38771       [
38772         "Montenegro (Crna Gora)",
38773         "me",
38774         "382"
38775       ],
38776       [
38777         "Montserrat",
38778         "ms",
38779         "1664"
38780       ],
38781       [
38782         "Morocco (‫المغرب‬‎)",
38783         "ma",
38784         "212",
38785         0
38786       ],
38787       [
38788         "Mozambique (Moçambique)",
38789         "mz",
38790         "258"
38791       ],
38792       [
38793         "Myanmar (Burma) (မြန်မာ)",
38794         "mm",
38795         "95"
38796       ],
38797       [
38798         "Namibia (Namibië)",
38799         "na",
38800         "264"
38801       ],
38802       [
38803         "Nauru",
38804         "nr",
38805         "674"
38806       ],
38807       [
38808         "Nepal (नेपाल)",
38809         "np",
38810         "977"
38811       ],
38812       [
38813         "Netherlands (Nederland)",
38814         "nl",
38815         "31"
38816       ],
38817       [
38818         "New Caledonia (Nouvelle-Calédonie)",
38819         "nc",
38820         "687"
38821       ],
38822       [
38823         "New Zealand",
38824         "nz",
38825         "64"
38826       ],
38827       [
38828         "Nicaragua",
38829         "ni",
38830         "505"
38831       ],
38832       [
38833         "Niger (Nijar)",
38834         "ne",
38835         "227"
38836       ],
38837       [
38838         "Nigeria",
38839         "ng",
38840         "234"
38841       ],
38842       [
38843         "Niue",
38844         "nu",
38845         "683"
38846       ],
38847       [
38848         "Norfolk Island",
38849         "nf",
38850         "672"
38851       ],
38852       [
38853         "North Korea (조선 민주주의 인민 공화국)",
38854         "kp",
38855         "850"
38856       ],
38857       [
38858         "Northern Mariana Islands",
38859         "mp",
38860         "1670"
38861       ],
38862       [
38863         "Norway (Norge)",
38864         "no",
38865         "47",
38866         0
38867       ],
38868       [
38869         "Oman (‫عُمان‬‎)",
38870         "om",
38871         "968"
38872       ],
38873       [
38874         "Pakistan (‫پاکستان‬‎)",
38875         "pk",
38876         "92"
38877       ],
38878       [
38879         "Palau",
38880         "pw",
38881         "680"
38882       ],
38883       [
38884         "Palestine (‫فلسطين‬‎)",
38885         "ps",
38886         "970"
38887       ],
38888       [
38889         "Panama (Panamá)",
38890         "pa",
38891         "507"
38892       ],
38893       [
38894         "Papua New Guinea",
38895         "pg",
38896         "675"
38897       ],
38898       [
38899         "Paraguay",
38900         "py",
38901         "595"
38902       ],
38903       [
38904         "Peru (Perú)",
38905         "pe",
38906         "51"
38907       ],
38908       [
38909         "Philippines",
38910         "ph",
38911         "63"
38912       ],
38913       [
38914         "Poland (Polska)",
38915         "pl",
38916         "48"
38917       ],
38918       [
38919         "Portugal",
38920         "pt",
38921         "351"
38922       ],
38923       [
38924         "Puerto Rico",
38925         "pr",
38926         "1",
38927         3,
38928         ["787", "939"]
38929       ],
38930       [
38931         "Qatar (‫قطر‬‎)",
38932         "qa",
38933         "974"
38934       ],
38935       [
38936         "Réunion (La Réunion)",
38937         "re",
38938         "262",
38939         0
38940       ],
38941       [
38942         "Romania (România)",
38943         "ro",
38944         "40"
38945       ],
38946       [
38947         "Russia (Россия)",
38948         "ru",
38949         "7",
38950         0
38951       ],
38952       [
38953         "Rwanda",
38954         "rw",
38955         "250"
38956       ],
38957       [
38958         "Saint Barthélemy",
38959         "bl",
38960         "590",
38961         1
38962       ],
38963       [
38964         "Saint Helena",
38965         "sh",
38966         "290"
38967       ],
38968       [
38969         "Saint Kitts and Nevis",
38970         "kn",
38971         "1869"
38972       ],
38973       [
38974         "Saint Lucia",
38975         "lc",
38976         "1758"
38977       ],
38978       [
38979         "Saint Martin (Saint-Martin (partie française))",
38980         "mf",
38981         "590",
38982         2
38983       ],
38984       [
38985         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38986         "pm",
38987         "508"
38988       ],
38989       [
38990         "Saint Vincent and the Grenadines",
38991         "vc",
38992         "1784"
38993       ],
38994       [
38995         "Samoa",
38996         "ws",
38997         "685"
38998       ],
38999       [
39000         "San Marino",
39001         "sm",
39002         "378"
39003       ],
39004       [
39005         "São Tomé and Príncipe (São Tomé e Príncipe)",
39006         "st",
39007         "239"
39008       ],
39009       [
39010         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39011         "sa",
39012         "966"
39013       ],
39014       [
39015         "Senegal (Sénégal)",
39016         "sn",
39017         "221"
39018       ],
39019       [
39020         "Serbia (Србија)",
39021         "rs",
39022         "381"
39023       ],
39024       [
39025         "Seychelles",
39026         "sc",
39027         "248"
39028       ],
39029       [
39030         "Sierra Leone",
39031         "sl",
39032         "232"
39033       ],
39034       [
39035         "Singapore",
39036         "sg",
39037         "65"
39038       ],
39039       [
39040         "Sint Maarten",
39041         "sx",
39042         "1721"
39043       ],
39044       [
39045         "Slovakia (Slovensko)",
39046         "sk",
39047         "421"
39048       ],
39049       [
39050         "Slovenia (Slovenija)",
39051         "si",
39052         "386"
39053       ],
39054       [
39055         "Solomon Islands",
39056         "sb",
39057         "677"
39058       ],
39059       [
39060         "Somalia (Soomaaliya)",
39061         "so",
39062         "252"
39063       ],
39064       [
39065         "South Africa",
39066         "za",
39067         "27"
39068       ],
39069       [
39070         "South Korea (대한민국)",
39071         "kr",
39072         "82"
39073       ],
39074       [
39075         "South Sudan (‫جنوب السودان‬‎)",
39076         "ss",
39077         "211"
39078       ],
39079       [
39080         "Spain (España)",
39081         "es",
39082         "34"
39083       ],
39084       [
39085         "Sri Lanka (ශ්‍රී ලංකාව)",
39086         "lk",
39087         "94"
39088       ],
39089       [
39090         "Sudan (‫السودان‬‎)",
39091         "sd",
39092         "249"
39093       ],
39094       [
39095         "Suriname",
39096         "sr",
39097         "597"
39098       ],
39099       [
39100         "Svalbard and Jan Mayen",
39101         "sj",
39102         "47",
39103         1
39104       ],
39105       [
39106         "Swaziland",
39107         "sz",
39108         "268"
39109       ],
39110       [
39111         "Sweden (Sverige)",
39112         "se",
39113         "46"
39114       ],
39115       [
39116         "Switzerland (Schweiz)",
39117         "ch",
39118         "41"
39119       ],
39120       [
39121         "Syria (‫سوريا‬‎)",
39122         "sy",
39123         "963"
39124       ],
39125       [
39126         "Taiwan (台灣)",
39127         "tw",
39128         "886"
39129       ],
39130       [
39131         "Tajikistan",
39132         "tj",
39133         "992"
39134       ],
39135       [
39136         "Tanzania",
39137         "tz",
39138         "255"
39139       ],
39140       [
39141         "Thailand (ไทย)",
39142         "th",
39143         "66"
39144       ],
39145       [
39146         "Timor-Leste",
39147         "tl",
39148         "670"
39149       ],
39150       [
39151         "Togo",
39152         "tg",
39153         "228"
39154       ],
39155       [
39156         "Tokelau",
39157         "tk",
39158         "690"
39159       ],
39160       [
39161         "Tonga",
39162         "to",
39163         "676"
39164       ],
39165       [
39166         "Trinidad and Tobago",
39167         "tt",
39168         "1868"
39169       ],
39170       [
39171         "Tunisia (‫تونس‬‎)",
39172         "tn",
39173         "216"
39174       ],
39175       [
39176         "Turkey (Türkiye)",
39177         "tr",
39178         "90"
39179       ],
39180       [
39181         "Turkmenistan",
39182         "tm",
39183         "993"
39184       ],
39185       [
39186         "Turks and Caicos Islands",
39187         "tc",
39188         "1649"
39189       ],
39190       [
39191         "Tuvalu",
39192         "tv",
39193         "688"
39194       ],
39195       [
39196         "U.S. Virgin Islands",
39197         "vi",
39198         "1340"
39199       ],
39200       [
39201         "Uganda",
39202         "ug",
39203         "256"
39204       ],
39205       [
39206         "Ukraine (Україна)",
39207         "ua",
39208         "380"
39209       ],
39210       [
39211         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39212         "ae",
39213         "971"
39214       ],
39215       [
39216         "United Kingdom",
39217         "gb",
39218         "44",
39219         0
39220       ],
39221       [
39222         "United States",
39223         "us",
39224         "1",
39225         0
39226       ],
39227       [
39228         "Uruguay",
39229         "uy",
39230         "598"
39231       ],
39232       [
39233         "Uzbekistan (Oʻzbekiston)",
39234         "uz",
39235         "998"
39236       ],
39237       [
39238         "Vanuatu",
39239         "vu",
39240         "678"
39241       ],
39242       [
39243         "Vatican City (Città del Vaticano)",
39244         "va",
39245         "39",
39246         1
39247       ],
39248       [
39249         "Venezuela",
39250         "ve",
39251         "58"
39252       ],
39253       [
39254         "Vietnam (Việt Nam)",
39255         "vn",
39256         "84"
39257       ],
39258       [
39259         "Wallis and Futuna (Wallis-et-Futuna)",
39260         "wf",
39261         "681"
39262       ],
39263       [
39264         "Western Sahara (‫الصحراء الغربية‬‎)",
39265         "eh",
39266         "212",
39267         1
39268       ],
39269       [
39270         "Yemen (‫اليمن‬‎)",
39271         "ye",
39272         "967"
39273       ],
39274       [
39275         "Zambia",
39276         "zm",
39277         "260"
39278       ],
39279       [
39280         "Zimbabwe",
39281         "zw",
39282         "263"
39283       ],
39284       [
39285         "Åland Islands",
39286         "ax",
39287         "358",
39288         1
39289       ]
39290   ];
39291   
39292   return d;
39293 }/**
39294 *    This script refer to:
39295 *    Title: International Telephone Input
39296 *    Author: Jack O'Connor
39297 *    Code version:  v12.1.12
39298 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39299 **/
39300
39301 /**
39302  * @class Roo.bootstrap.PhoneInput
39303  * @extends Roo.bootstrap.TriggerField
39304  * An input with International dial-code selection
39305  
39306  * @cfg {String} defaultDialCode default '+852'
39307  * @cfg {Array} preferedCountries default []
39308   
39309  * @constructor
39310  * Create a new PhoneInput.
39311  * @param {Object} config Configuration options
39312  */
39313
39314 Roo.bootstrap.PhoneInput = function(config) {
39315     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39316 };
39317
39318 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39319         
39320         listWidth: undefined,
39321         
39322         selectedClass: 'active',
39323         
39324         invalidClass : "has-warning",
39325         
39326         validClass: 'has-success',
39327         
39328         allowed: '0123456789',
39329         
39330         /**
39331          * @cfg {String} defaultDialCode The default dial code when initializing the input
39332          */
39333         defaultDialCode: '+852',
39334         
39335         /**
39336          * @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
39337          */
39338         preferedCountries: false,
39339         
39340         getAutoCreate : function()
39341         {
39342             var data = Roo.bootstrap.PhoneInputData();
39343             var align = this.labelAlign || this.parentLabelAlign();
39344             var id = Roo.id();
39345             
39346             this.allCountries = [];
39347             this.dialCodeMapping = [];
39348             
39349             for (var i = 0; i < data.length; i++) {
39350               var c = data[i];
39351               this.allCountries[i] = {
39352                 name: c[0],
39353                 iso2: c[1],
39354                 dialCode: c[2],
39355                 priority: c[3] || 0,
39356                 areaCodes: c[4] || null
39357               };
39358               this.dialCodeMapping[c[2]] = {
39359                   name: c[0],
39360                   iso2: c[1],
39361                   priority: c[3] || 0,
39362                   areaCodes: c[4] || null
39363               };
39364             }
39365             
39366             var cfg = {
39367                 cls: 'form-group',
39368                 cn: []
39369             };
39370             
39371             var input =  {
39372                 tag: 'input',
39373                 id : id,
39374                 cls : 'form-control tel-input',
39375                 autocomplete: 'new-password'
39376             };
39377             
39378             var hiddenInput = {
39379                 tag: 'input',
39380                 type: 'hidden',
39381                 cls: 'hidden-tel-input'
39382             };
39383             
39384             if (this.name) {
39385                 hiddenInput.name = this.name;
39386             }
39387             
39388             if (this.disabled) {
39389                 input.disabled = true;
39390             }
39391             
39392             var flag_container = {
39393                 tag: 'div',
39394                 cls: 'flag-box',
39395                 cn: [
39396                     {
39397                         tag: 'div',
39398                         cls: 'flag'
39399                     },
39400                     {
39401                         tag: 'div',
39402                         cls: 'caret'
39403                     }
39404                 ]
39405             };
39406             
39407             var box = {
39408                 tag: 'div',
39409                 cls: this.hasFeedback ? 'has-feedback' : '',
39410                 cn: [
39411                     hiddenInput,
39412                     input,
39413                     {
39414                         tag: 'input',
39415                         cls: 'dial-code-holder',
39416                         disabled: true
39417                     }
39418                 ]
39419             };
39420             
39421             var container = {
39422                 cls: 'roo-select2-container input-group',
39423                 cn: [
39424                     flag_container,
39425                     box
39426                 ]
39427             };
39428             
39429             if (this.fieldLabel.length) {
39430                 var indicator = {
39431                     tag: 'i',
39432                     tooltip: 'This field is required'
39433                 };
39434                 
39435                 var label = {
39436                     tag: 'label',
39437                     'for':  id,
39438                     cls: 'control-label',
39439                     cn: []
39440                 };
39441                 
39442                 var label_text = {
39443                     tag: 'span',
39444                     html: this.fieldLabel
39445                 };
39446                 
39447                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39448                 label.cn = [
39449                     indicator,
39450                     label_text
39451                 ];
39452                 
39453                 if(this.indicatorpos == 'right') {
39454                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39455                     label.cn = [
39456                         label_text,
39457                         indicator
39458                     ];
39459                 }
39460                 
39461                 if(align == 'left') {
39462                     container = {
39463                         tag: 'div',
39464                         cn: [
39465                             container
39466                         ]
39467                     };
39468                     
39469                     if(this.labelWidth > 12){
39470                         label.style = "width: " + this.labelWidth + 'px';
39471                     }
39472                     if(this.labelWidth < 13 && this.labelmd == 0){
39473                         this.labelmd = this.labelWidth;
39474                     }
39475                     if(this.labellg > 0){
39476                         label.cls += ' col-lg-' + this.labellg;
39477                         input.cls += ' col-lg-' + (12 - this.labellg);
39478                     }
39479                     if(this.labelmd > 0){
39480                         label.cls += ' col-md-' + this.labelmd;
39481                         container.cls += ' col-md-' + (12 - this.labelmd);
39482                     }
39483                     if(this.labelsm > 0){
39484                         label.cls += ' col-sm-' + this.labelsm;
39485                         container.cls += ' col-sm-' + (12 - this.labelsm);
39486                     }
39487                     if(this.labelxs > 0){
39488                         label.cls += ' col-xs-' + this.labelxs;
39489                         container.cls += ' col-xs-' + (12 - this.labelxs);
39490                     }
39491                 }
39492             }
39493             
39494             cfg.cn = [
39495                 label,
39496                 container
39497             ];
39498             
39499             var settings = this;
39500             
39501             ['xs','sm','md','lg'].map(function(size){
39502                 if (settings[size]) {
39503                     cfg.cls += ' col-' + size + '-' + settings[size];
39504                 }
39505             });
39506             
39507             this.store = new Roo.data.Store({
39508                 proxy : new Roo.data.MemoryProxy({}),
39509                 reader : new Roo.data.JsonReader({
39510                     fields : [
39511                         {
39512                             'name' : 'name',
39513                             'type' : 'string'
39514                         },
39515                         {
39516                             'name' : 'iso2',
39517                             'type' : 'string'
39518                         },
39519                         {
39520                             'name' : 'dialCode',
39521                             'type' : 'string'
39522                         },
39523                         {
39524                             'name' : 'priority',
39525                             'type' : 'string'
39526                         },
39527                         {
39528                             'name' : 'areaCodes',
39529                             'type' : 'string'
39530                         }
39531                     ]
39532                 })
39533             });
39534             
39535             if(!this.preferedCountries) {
39536                 this.preferedCountries = [
39537                     'hk',
39538                     'gb',
39539                     'us'
39540                 ];
39541             }
39542             
39543             var p = this.preferedCountries.reverse();
39544             
39545             if(p) {
39546                 for (var i = 0; i < p.length; i++) {
39547                     for (var j = 0; j < this.allCountries.length; j++) {
39548                         if(this.allCountries[j].iso2 == p[i]) {
39549                             var t = this.allCountries[j];
39550                             this.allCountries.splice(j,1);
39551                             this.allCountries.unshift(t);
39552                         }
39553                     } 
39554                 }
39555             }
39556             
39557             this.store.proxy.data = {
39558                 success: true,
39559                 data: this.allCountries
39560             };
39561             
39562             return cfg;
39563         },
39564         
39565         initEvents : function()
39566         {
39567             this.createList();
39568             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39569             
39570             this.indicator = this.indicatorEl();
39571             this.flag = this.flagEl();
39572             this.dialCodeHolder = this.dialCodeHolderEl();
39573             
39574             this.trigger = this.el.select('div.flag-box',true).first();
39575             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39576             
39577             var _this = this;
39578             
39579             (function(){
39580                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39581                 _this.list.setWidth(lw);
39582             }).defer(100);
39583             
39584             this.list.on('mouseover', this.onViewOver, this);
39585             this.list.on('mousemove', this.onViewMove, this);
39586             this.inputEl().on("keyup", this.onKeyUp, this);
39587             
39588             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39589
39590             this.view = new Roo.View(this.list, this.tpl, {
39591                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39592             });
39593             
39594             this.view.on('click', this.onViewClick, this);
39595             this.setValue(this.defaultDialCode);
39596         },
39597         
39598         onTriggerClick : function(e)
39599         {
39600             Roo.log('trigger click');
39601             if(this.disabled){
39602                 return;
39603             }
39604             
39605             if(this.isExpanded()){
39606                 this.collapse();
39607                 this.hasFocus = false;
39608             }else {
39609                 this.store.load({});
39610                 this.hasFocus = true;
39611                 this.expand();
39612             }
39613         },
39614         
39615         isExpanded : function()
39616         {
39617             return this.list.isVisible();
39618         },
39619         
39620         collapse : function()
39621         {
39622             if(!this.isExpanded()){
39623                 return;
39624             }
39625             this.list.hide();
39626             Roo.get(document).un('mousedown', this.collapseIf, this);
39627             Roo.get(document).un('mousewheel', this.collapseIf, this);
39628             this.fireEvent('collapse', this);
39629             this.validate();
39630         },
39631         
39632         expand : function()
39633         {
39634             Roo.log('expand');
39635
39636             if(this.isExpanded() || !this.hasFocus){
39637                 return;
39638             }
39639             
39640             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39641             this.list.setWidth(lw);
39642             
39643             this.list.show();
39644             this.restrictHeight();
39645             
39646             Roo.get(document).on('mousedown', this.collapseIf, this);
39647             Roo.get(document).on('mousewheel', this.collapseIf, this);
39648             
39649             this.fireEvent('expand', this);
39650         },
39651         
39652         restrictHeight : function()
39653         {
39654             this.list.alignTo(this.inputEl(), this.listAlign);
39655             this.list.alignTo(this.inputEl(), this.listAlign);
39656         },
39657         
39658         onViewOver : function(e, t)
39659         {
39660             if(this.inKeyMode){
39661                 return;
39662             }
39663             var item = this.view.findItemFromChild(t);
39664             
39665             if(item){
39666                 var index = this.view.indexOf(item);
39667                 this.select(index, false);
39668             }
39669         },
39670
39671         // private
39672         onViewClick : function(view, doFocus, el, e)
39673         {
39674             var index = this.view.getSelectedIndexes()[0];
39675             
39676             var r = this.store.getAt(index);
39677             
39678             if(r){
39679                 this.onSelect(r, index);
39680             }
39681             if(doFocus !== false && !this.blockFocus){
39682                 this.inputEl().focus();
39683             }
39684         },
39685         
39686         onViewMove : function(e, t)
39687         {
39688             this.inKeyMode = false;
39689         },
39690         
39691         select : function(index, scrollIntoView)
39692         {
39693             this.selectedIndex = index;
39694             this.view.select(index);
39695             if(scrollIntoView !== false){
39696                 var el = this.view.getNode(index);
39697                 if(el){
39698                     this.list.scrollChildIntoView(el, false);
39699                 }
39700             }
39701         },
39702         
39703         createList : function()
39704         {
39705             this.list = Roo.get(document.body).createChild({
39706                 tag: 'ul',
39707                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39708                 style: 'display:none'
39709             });
39710             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39711         },
39712         
39713         collapseIf : function(e)
39714         {
39715             var in_combo  = e.within(this.el);
39716             var in_list =  e.within(this.list);
39717             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39718             
39719             if (in_combo || in_list || is_list) {
39720                 return;
39721             }
39722             this.collapse();
39723         },
39724         
39725         onSelect : function(record, index)
39726         {
39727             if(this.fireEvent('beforeselect', this, record, index) !== false){
39728                 
39729                 this.setFlagClass(record.data.iso2);
39730                 this.setDialCode(record.data.dialCode);
39731                 this.hasFocus = false;
39732                 this.collapse();
39733                 this.fireEvent('select', this, record, index);
39734             }
39735         },
39736         
39737         flagEl : function()
39738         {
39739             var flag = this.el.select('div.flag',true).first();
39740             if(!flag){
39741                 return false;
39742             }
39743             return flag;
39744         },
39745         
39746         dialCodeHolderEl : function()
39747         {
39748             var d = this.el.select('input.dial-code-holder',true).first();
39749             if(!d){
39750                 return false;
39751             }
39752             return d;
39753         },
39754         
39755         setDialCode : function(v)
39756         {
39757             this.dialCodeHolder.dom.value = '+'+v;
39758         },
39759         
39760         setFlagClass : function(n)
39761         {
39762             this.flag.dom.className = 'flag '+n;
39763         },
39764         
39765         getValue : function()
39766         {
39767             var v = this.inputEl().getValue();
39768             if(this.dialCodeHolder) {
39769                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39770             }
39771             return v;
39772         },
39773         
39774         setValue : function(v)
39775         {
39776             var d = this.getDialCode(v);
39777             
39778             //invalid dial code
39779             if(v.length == 0 || !d || d.length == 0) {
39780                 if(this.rendered){
39781                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39782                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39783                 }
39784                 return;
39785             }
39786             
39787             //valid dial code
39788             this.setFlagClass(this.dialCodeMapping[d].iso2);
39789             this.setDialCode(d);
39790             this.inputEl().dom.value = v.replace('+'+d,'');
39791             this.hiddenEl().dom.value = this.getValue();
39792             
39793             this.validate();
39794         },
39795         
39796         getDialCode : function(v = '')
39797         {
39798             if (v.length == 0) {
39799                 return this.dialCodeHolder.dom.value;
39800             }
39801             
39802             var dialCode = "";
39803             if (v.charAt(0) != "+") {
39804                 return false;
39805             }
39806             var numericChars = "";
39807             for (var i = 1; i < v.length; i++) {
39808               var c = v.charAt(i);
39809               if (!isNaN(c)) {
39810                 numericChars += c;
39811                 if (this.dialCodeMapping[numericChars]) {
39812                   dialCode = v.substr(1, i);
39813                 }
39814                 if (numericChars.length == 4) {
39815                   break;
39816                 }
39817               }
39818             }
39819             return dialCode;
39820         },
39821         
39822         reset : function()
39823         {
39824             this.setValue(this.defaultDialCode);
39825             this.validate();
39826         },
39827         
39828         hiddenEl : function()
39829         {
39830             return this.el.select('input.hidden-tel-input',true).first();
39831         },
39832         
39833         onKeyUp : function(e){
39834             
39835             var k = e.getKey();
39836             var c = e.getCharCode();
39837             
39838             if(
39839                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39840                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39841             ){
39842                 e.stopEvent();
39843             }
39844             
39845             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39846             //     return;
39847             // }
39848             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39849                 e.stopEvent();
39850             }
39851             
39852             this.setValue(this.getValue());
39853         }
39854         
39855 });
39856 /**
39857  * @class Roo.bootstrap.MoneyField
39858  * @extends Roo.bootstrap.ComboBox
39859  * Bootstrap MoneyField class
39860  * 
39861  * @constructor
39862  * Create a new MoneyField.
39863  * @param {Object} config Configuration options
39864  */
39865
39866 Roo.bootstrap.MoneyField = function(config) {
39867     
39868     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39869     
39870 };
39871
39872 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39873     
39874     /**
39875      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39876      */
39877     allowDecimals : true,
39878     /**
39879      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39880      */
39881     decimalSeparator : ".",
39882     /**
39883      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39884      */
39885     decimalPrecision : 2,
39886     /**
39887      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39888      */
39889     allowNegative : true,
39890     /**
39891      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39892      */
39893     minValue : Number.NEGATIVE_INFINITY,
39894     /**
39895      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39896      */
39897     maxValue : Number.MAX_VALUE,
39898     /**
39899      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39900      */
39901     minText : "The minimum value for this field is {0}",
39902     /**
39903      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39904      */
39905     maxText : "The maximum value for this field is {0}",
39906     /**
39907      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39908      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39909      */
39910     nanText : "{0} is not a valid number",
39911     /**
39912      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39913      */
39914     castInt : true,
39915     
39916     inputlg : 9,
39917     inputmd : 9,
39918     inputsm : 9,
39919     inputxs : 6,
39920     
39921     store : false,
39922     
39923     getAutoCreate : function()
39924     {
39925         var align = this.labelAlign || this.parentLabelAlign();
39926         
39927         var id = Roo.id();
39928
39929         var cfg = {
39930             cls: 'form-group',
39931             cn: []
39932         };
39933
39934         var input =  {
39935             tag: 'input',
39936             id : id,
39937             cls : 'form-control roo-money-amount-input',
39938             autocomplete: 'new-password'
39939         };
39940         
39941         if (this.name) {
39942             input.name = this.name;
39943         }
39944
39945         if (this.disabled) {
39946             input.disabled = true;
39947         }
39948
39949         var clg = 12 - this.inputlg;
39950         var cmd = 12 - this.inputmd;
39951         var csm = 12 - this.inputsm;
39952         var cxs = 12 - this.inputxs;
39953         
39954         var container = {
39955             tag : 'div',
39956             cls : 'row roo-money-field',
39957             cn : [
39958                 {
39959                     tag : 'div',
39960                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39961                     cn : [
39962                         {
39963                             tag : 'div',
39964                             cls: 'roo-select2-container input-group',
39965                             cn: [
39966                                 {
39967                                     tag : 'input',
39968                                     cls : 'form-control roo-money-currency-input',
39969                                     autocomplete: 'new-password',
39970                                     readOnly : 1,
39971                                     name : this.currencyName
39972                                 },
39973                                 {
39974                                     tag :'span',
39975                                     cls : 'input-group-addon',
39976                                     cn : [
39977                                         {
39978                                             tag: 'span',
39979                                             cls: 'caret'
39980                                         }
39981                                     ]
39982                                 }
39983                             ]
39984                         }
39985                     ]
39986                 },
39987                 {
39988                     tag : 'div',
39989                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39990                     cn : [
39991                         {
39992                             tag: 'div',
39993                             cls: this.hasFeedback ? 'has-feedback' : '',
39994                             cn: [
39995                                 input
39996                             ]
39997                         }
39998                     ]
39999                 }
40000             ]
40001             
40002         };
40003         
40004         if (this.fieldLabel.length) {
40005             var indicator = {
40006                 tag: 'i',
40007                 tooltip: 'This field is required'
40008             };
40009
40010             var label = {
40011                 tag: 'label',
40012                 'for':  id,
40013                 cls: 'control-label',
40014                 cn: []
40015             };
40016
40017             var label_text = {
40018                 tag: 'span',
40019                 html: this.fieldLabel
40020             };
40021
40022             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40023             label.cn = [
40024                 indicator,
40025                 label_text
40026             ];
40027
40028             if(this.indicatorpos == 'right') {
40029                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40030                 label.cn = [
40031                     label_text,
40032                     indicator
40033                 ];
40034             }
40035
40036             if(align == 'left') {
40037                 container = {
40038                     tag: 'div',
40039                     cn: [
40040                         container
40041                     ]
40042                 };
40043
40044                 if(this.labelWidth > 12){
40045                     label.style = "width: " + this.labelWidth + 'px';
40046                 }
40047                 if(this.labelWidth < 13 && this.labelmd == 0){
40048                     this.labelmd = this.labelWidth;
40049                 }
40050                 if(this.labellg > 0){
40051                     label.cls += ' col-lg-' + this.labellg;
40052                     input.cls += ' col-lg-' + (12 - this.labellg);
40053                 }
40054                 if(this.labelmd > 0){
40055                     label.cls += ' col-md-' + this.labelmd;
40056                     container.cls += ' col-md-' + (12 - this.labelmd);
40057                 }
40058                 if(this.labelsm > 0){
40059                     label.cls += ' col-sm-' + this.labelsm;
40060                     container.cls += ' col-sm-' + (12 - this.labelsm);
40061                 }
40062                 if(this.labelxs > 0){
40063                     label.cls += ' col-xs-' + this.labelxs;
40064                     container.cls += ' col-xs-' + (12 - this.labelxs);
40065                 }
40066             }
40067         }
40068
40069         cfg.cn = [
40070             label,
40071             container
40072         ];
40073
40074         var settings = this;
40075
40076         ['xs','sm','md','lg'].map(function(size){
40077             if (settings[size]) {
40078                 cfg.cls += ' col-' + size + '-' + settings[size];
40079             }
40080         });
40081         
40082         return cfg;
40083         
40084     },
40085     
40086     initEvents : function()
40087     {
40088         this.indicator = this.indicatorEl();
40089         
40090         this.initCurrencyEvent();
40091         
40092         this.initNumberEvent();
40093         
40094     },
40095     
40096     initCurrencyEvent : function()
40097     {
40098         if (!this.store) {
40099             throw "can not find store for combo";
40100         }
40101         
40102         this.store = Roo.factory(this.store, Roo.data);
40103         this.store.parent = this;
40104         
40105         this.createList();
40106         
40107         this.triggerEl = this.el.select('.input-group-addon', true).first();
40108         
40109         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40110         
40111         var _this = this;
40112         
40113         (function(){
40114             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40115             _this.list.setWidth(lw);
40116         }).defer(100);
40117         
40118         this.list.on('mouseover', this.onViewOver, this);
40119         this.list.on('mousemove', this.onViewMove, this);
40120         this.list.on('scroll', this.onViewScroll, this);
40121         
40122         if(!this.tpl){
40123             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40124         }
40125         
40126         this.view = new Roo.View(this.list, this.tpl, {
40127             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40128         });
40129         
40130         this.view.on('click', this.onViewClick, this);
40131         
40132         this.store.on('beforeload', this.onBeforeLoad, this);
40133         this.store.on('load', this.onLoad, this);
40134         this.store.on('loadexception', this.onLoadException, this);
40135         
40136         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40137             "up" : function(e){
40138                 this.inKeyMode = true;
40139                 this.selectPrev();
40140             },
40141
40142             "down" : function(e){
40143                 if(!this.isExpanded()){
40144                     this.onTriggerClick();
40145                 }else{
40146                     this.inKeyMode = true;
40147                     this.selectNext();
40148                 }
40149             },
40150
40151             "enter" : function(e){
40152                 this.collapse();
40153                 
40154                 if(this.fireEvent("specialkey", this, e)){
40155                     this.onViewClick(false);
40156                 }
40157                 
40158                 return true;
40159             },
40160
40161             "esc" : function(e){
40162                 this.collapse();
40163             },
40164
40165             "tab" : function(e){
40166                 this.collapse();
40167                 
40168                 if(this.fireEvent("specialkey", this, e)){
40169                     this.onViewClick(false);
40170                 }
40171                 
40172                 return true;
40173             },
40174
40175             scope : this,
40176
40177             doRelay : function(foo, bar, hname){
40178                 if(hname == 'down' || this.scope.isExpanded()){
40179                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40180                 }
40181                 return true;
40182             },
40183
40184             forceKeyDown: true
40185         });
40186         
40187         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40188         
40189     },
40190     
40191     initNumberEvent : function(e)
40192     {
40193         this.inputEl().on("keydown" , this.fireKey,  this);
40194         this.inputEl().on("focus", this.onFocus,  this);
40195         this.inputEl().on("blur", this.onBlur,  this);
40196         
40197         this.inputEl().relayEvent('keyup', this);
40198         
40199         if(this.indicator){
40200             this.indicator.addClass('invisible');
40201         }
40202  
40203         this.originalValue = this.getValue();
40204         
40205         if(this.validationEvent == 'keyup'){
40206             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40207             this.inputEl().on('keyup', this.filterValidation, this);
40208         }
40209         else if(this.validationEvent !== false){
40210             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40211         }
40212         
40213         if(this.selectOnFocus){
40214             this.on("focus", this.preFocus, this);
40215             
40216         }
40217         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40218             this.inputEl().on("keypress", this.filterKeys, this);
40219         } else {
40220             this.inputEl().relayEvent('keypress', this);
40221         }
40222         
40223         var allowed = "0123456789";
40224         
40225         if(this.allowDecimals){
40226             allowed += this.decimalSeparator;
40227         }
40228         
40229         if(this.allowNegative){
40230             allowed += "-";
40231         }
40232         
40233         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40234         
40235         var keyPress = function(e){
40236             
40237             var k = e.getKey();
40238             
40239             var c = e.getCharCode();
40240             
40241             if(
40242                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40243                     allowed.indexOf(String.fromCharCode(c)) === -1
40244             ){
40245                 e.stopEvent();
40246                 return;
40247             }
40248             
40249             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40250                 return;
40251             }
40252             
40253             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40254                 e.stopEvent();
40255             }
40256         };
40257         
40258         this.inputEl().on("keypress", keyPress, this);
40259         
40260     },
40261     
40262     onTriggerClick : function(e)
40263     {   
40264         if(this.disabled){
40265             return;
40266         }
40267         
40268         this.page = 0;
40269         this.loadNext = false;
40270         
40271         if(this.isExpanded()){
40272             this.collapse();
40273             return;
40274         }
40275         
40276         this.hasFocus = true;
40277         
40278         if(this.triggerAction == 'all') {
40279             this.doQuery(this.allQuery, true);
40280             return;
40281         }
40282         
40283         this.doQuery(this.getRawValue());
40284     },
40285     
40286     getCurrency : function()
40287     {   
40288         var v = this.currencyEl().getValue();
40289         
40290         return v;
40291     },
40292     
40293     restrictHeight : function()
40294     {
40295         this.list.alignTo(this.currencyEl(), this.listAlign);
40296         this.list.alignTo(this.currencyEl(), this.listAlign);
40297     },
40298     
40299     onViewClick : function(view, doFocus, el, e)
40300     {
40301         var index = this.view.getSelectedIndexes()[0];
40302         
40303         var r = this.store.getAt(index);
40304         
40305         if(r){
40306             this.onSelect(r, index);
40307         }
40308     },
40309     
40310     onSelect : function(record, index){
40311         
40312         if(this.fireEvent('beforeselect', this, record, index) !== false){
40313         
40314             this.setFromCurrencyData(index > -1 ? record.data : false);
40315             
40316             this.collapse();
40317             
40318             this.fireEvent('select', this, record, index);
40319         }
40320     },
40321     
40322     setFromCurrencyData : function(o)
40323     {
40324         var currency = '';
40325         
40326         this.lastCurrency = o;
40327         
40328         if (this.currencyField) {
40329             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40330         } else {
40331             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40332         }
40333         
40334         this.lastSelectionText = currency;
40335         
40336         this.setCurrency(currency);
40337     },
40338     
40339     setFromData : function(o)
40340     {
40341         var c = {};
40342         
40343         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40344         
40345         this.setFromCurrencyData(c);
40346         
40347         var value = '';
40348         
40349         if (this.name) {
40350             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40351         } else {
40352             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40353         }
40354         
40355         this.setValue(value);
40356         
40357     },
40358     
40359     setCurrency : function(v)
40360     {   
40361         this.currencyValue = v;
40362         
40363         if(this.rendered){
40364             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40365             this.validate();
40366         }
40367     },
40368     
40369     setValue : function(v)
40370     {
40371         v = this.fixPrecision(v);
40372         
40373         v = String(v).replace(".", this.decimalSeparator);
40374         
40375         this.value = v;
40376         
40377         if(this.rendered){
40378             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40379             this.validate();
40380         }
40381     },
40382     
40383     getRawValue : function()
40384     {
40385         var v = this.inputEl().getValue();
40386         
40387         return v;
40388     },
40389     
40390     getValue : function()
40391     {
40392         return this.fixPrecision(this.parseValue(this.getRawValue()));
40393     },
40394     
40395     parseValue : function(value)
40396     {
40397         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40398         return isNaN(value) ? '' : value;
40399     },
40400     
40401     fixPrecision : function(value)
40402     {
40403         var nan = isNaN(value);
40404         
40405         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40406             return nan ? '' : value;
40407         }
40408         
40409         return parseFloat(value).toFixed(this.decimalPrecision);
40410     },
40411     
40412     decimalPrecisionFcn : function(v)
40413     {
40414         return Math.floor(v);
40415     },
40416     
40417     validateValue : function(value)
40418     {
40419         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40420             return false;
40421         }
40422         
40423         var num = this.parseValue(value);
40424         
40425         if(isNaN(num)){
40426             this.markInvalid(String.format(this.nanText, value));
40427             return false;
40428         }
40429         
40430         if(num < this.minValue){
40431             this.markInvalid(String.format(this.minText, this.minValue));
40432             return false;
40433         }
40434         
40435         if(num > this.maxValue){
40436             this.markInvalid(String.format(this.maxText, this.maxValue));
40437             return false;
40438         }
40439         
40440         return true;
40441     },
40442     
40443     validate : function()
40444     {
40445         if(this.disabled || this.allowBlank){
40446             this.markValid();
40447             return true;
40448         }
40449         
40450         var currency = this.getCurrency();
40451         
40452         if(this.validateValue(this.getRawValue()) && currency.length){
40453             this.markValid();
40454             return true;
40455         }
40456         
40457         this.markInvalid();
40458         return false;
40459     },
40460     
40461     getName: function()
40462     {
40463         return this.name;
40464     },
40465     
40466     beforeBlur : function()
40467     {
40468         if(!this.castInt){
40469             return;
40470         }
40471         
40472         var v = this.parseValue(this.getRawValue());
40473         
40474         if(v){
40475             this.setValue(v);
40476         }
40477     },
40478     
40479     onBlur : function()
40480     {
40481         this.beforeBlur();
40482         
40483         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40484             //this.el.removeClass(this.focusClass);
40485         }
40486         
40487         this.hasFocus = false;
40488         
40489         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40490             this.validate();
40491         }
40492         
40493         var v = this.getValue();
40494         
40495         if(String(v) !== String(this.startValue)){
40496             this.fireEvent('change', this, v, this.startValue);
40497         }
40498         
40499         this.fireEvent("blur", this);
40500     },
40501     
40502     inputEl : function()
40503     {
40504         return this.el.select('.roo-money-amount-input', true).first();
40505     },
40506     
40507     currencyEl : function()
40508     {
40509         return this.el.select('.roo-money-currency-input', true).first();
40510     }
40511     
40512 });